From b0284c8d9e33e3a96cacd1724fbc36f192ea18f0 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 3 Mar 2019 20:53:59 +0000 Subject: [PATCH] Improve type and length safety of commands taking binary data --- src/command.cpp | 25 +++++++++++++------------ src/command_func.h | 2 +- src/command_type.h | 20 +++++++++++++++++++- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index de45540922..d7aee1cd30 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -260,7 +260,7 @@ CommandProc CmdRenamePlan; CommandProc CmdDesyncCheck; -#define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type} +#define DEF_CMD(proc, flags, type) Command(proc, #proc, (CommandFlags)flags, type) /** * The master command table @@ -638,7 +638,7 @@ static int _docommand_recursive = 0; */ CommandCost DoCommand(const CommandContainer *container, DoCommandFlag flags) { - return DoCommand(container->tile, container->p1, container->p2, flags, container->cmd & CMD_ID_MASK, container->text.c_str()); + return DoCommand(container->tile, container->p1, container->p2, flags, container->cmd & CMD_ID_MASK, container->text.c_str(), container->binary_length); } /*! @@ -651,10 +651,11 @@ CommandCost DoCommand(const CommandContainer *container, DoCommandFlag flags) * @param flags Flags for the command and how to execute the command * @param cmd The command-id to execute (a value of the CMD_* enums) * @param text The text to pass + * @param binary_length The length of binary data in text * @see CommandProc * @return the cost */ -CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, uint32 cmd, const char *text) +CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, uint32 cmd, const char *text, uint32 binary_length) { SCOPE_INFO_FMT([=], "DoCommand: tile: %X (%d x %d), p1: 0x%X, p2: 0x%X, flags: 0x%X, company: %s, cmd: 0x%X (%s)", tile, TileX(tile), TileY(tile), p1, p2, flags, scope_dumper().CompanyInfo(_current_company), cmd, GetCommandName(cmd)); @@ -665,7 +666,7 @@ CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, if (tile != 0 && (tile >= MapSize() || (!IsValidTile(tile) && (flags & DC_ALL_TILES) == 0))) return CMD_ERROR; /* Chop of any CMD_MSG or other flags; we don't need those here */ - CommandProc *proc = _command_proc_table[cmd & CMD_ID_MASK].proc; + const Command &command = _command_proc_table[cmd & CMD_ID_MASK]; _docommand_recursive++; @@ -673,7 +674,7 @@ CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, if (_docommand_recursive == 1 || !(flags & DC_EXEC) ) { if (_docommand_recursive == 1) _cleared_object_areas.Clear(); SetTownRatingTestMode(true); - res = proc(tile, flags & ~DC_EXEC, p1, p2, text); + res = command.Execute(tile, flags & ~DC_EXEC, p1, p2, text, binary_length); SetTownRatingTestMode(false); if (res.Failed()) { goto error; @@ -695,7 +696,7 @@ CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, /* Execute the command here. All cost-relevant functions set the expenses type * themselves to the cost object at some point */ if (_docommand_recursive == 1) _cleared_object_areas.Clear(); - res = proc(tile, flags, p1, p2, text); + res = command.Execute(tile, flags, p1, p2, text, binary_length); if (res.Failed()) { error: _docommand_recursive--; @@ -732,7 +733,7 @@ Money GetAvailableMoneyForCommand() */ bool DoCommandP(const CommandContainer *container, bool my_cmd) { - return DoCommandP(container->tile, container->p1, container->p2, container->cmd, container->callback, container->text.c_str(), my_cmd); + return DoCommandP(container->tile, container->p1, container->p2, container->cmd, container->callback, container->text.c_str(), my_cmd, container->binary_length); } /*! @@ -748,7 +749,7 @@ bool DoCommandP(const CommandContainer *container, bool my_cmd) * @param callback A callback function to call after the command is finished * @param text The text to pass * @param my_cmd indicator if the command is from a company or server (to display error messages for a user) - * @param binary_length The quantity of binary data in text + * @param binary_length The length of binary data in text * @return \c true if the command succeeded, else \c false. */ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd, uint32 binary_length) @@ -853,10 +854,10 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, byte cmd_id = cmd & CMD_ID_MASK; assert(cmd_id < lengthof(_command_proc_table)); - CommandProc *proc = _command_proc_table[cmd_id].proc; + const Command &command = _command_proc_table[cmd_id]; /* Shouldn't happen, but you never know when someone adds * NULLs to the _command_proc_table. */ - assert(proc != NULL); + assert(command.proc != NULL); /* Command flags are used internally */ CommandFlags cmd_flags = GetCommandFlags(cmd); @@ -890,7 +891,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, _cleared_object_areas.Clear(); SetTownRatingTestMode(true); BasePersistentStorageArray::SwitchMode(PSM_ENTER_TESTMODE); - CommandCost res = proc(tile, flags, p1, p2, text); + CommandCost res = command.Execute(tile, flags, p1, p2, text, binary_length); BasePersistentStorageArray::SwitchMode(PSM_LEAVE_TESTMODE); SetTownRatingTestMode(false); @@ -935,7 +936,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, * use the construction one */ _cleared_object_areas.Clear(); BasePersistentStorageArray::SwitchMode(PSM_ENTER_COMMAND); - CommandCost res2 = proc(tile, flags | DC_EXEC, p1, p2, text); + CommandCost res2 = command.Execute(tile, flags | DC_EXEC, p1, p2, text, binary_length); BasePersistentStorageArray::SwitchMode(PSM_LEAVE_COMMAND); if (cmd_id == CMD_COMPANY_CTRL) { diff --git a/src/command_func.h b/src/command_func.h index 5c585f50b0..4c03c222ef 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -34,7 +34,7 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID); */ #define return_cmd_error(errcode) return CommandCost(errcode); -CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, uint32 cmd, const char *text = NULL); +CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, uint32 cmd, const char *text = NULL, uint32 binary_length = 0); CommandCost DoCommand(const CommandContainer *container, DoCommandFlag flags); bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback = NULL, const char *text = NULL, bool my_cmd = true, uint32 binary_length = 0); diff --git a/src/command_type.h b/src/command_type.h index 73bcc6d558..e10e50678c 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -492,6 +492,7 @@ enum CommandFlags { CMD_DEITY = 0x100, ///< the command may be executed by COMPANY_DEITY CMD_STR_CTRL = 0x200, ///< the command's string may contain control strings CMD_NO_EST = 0x400, ///< the command is never estimated. + CMD_PROCEX = 0x800, ///< the command proc function has extended parameters }; DECLARE_ENUM_AS_BIT_SET(CommandFlags) @@ -537,6 +538,7 @@ enum CommandPauseLevel { * @return The CommandCost of the command, which can be succeeded or failed. */ typedef CommandCost CommandProc(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); +typedef CommandCost CommandProcEx(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text, uint32 binary_length); /** * Define a command with the flags which belongs to it. @@ -545,10 +547,26 @@ typedef CommandCost CommandProc(TileIndex tile, DoCommandFlag flags, uint32 p1, * the #CMD_AUTO, #CMD_OFFLINE and #CMD_SERVER values. */ struct Command { - CommandProc *proc; ///< The procedure to actually executing + union { + CommandProc *proc; ///< The procedure to actually execute + CommandProcEx *procex; ///< The procedure to actually execute, extended parameters + }; const char *name; ///< A human readable name for the procedure CommandFlags flags; ///< The (command) flags to that apply to this command CommandType type; ///< The type of command. + + Command(CommandProc *proc, const char *name, CommandFlags flags, CommandType type) + : proc(proc), name(name), flags(flags & ~CMD_PROCEX), type(type) {} + Command(CommandProcEx *procex, const char *name, CommandFlags flags, CommandType type) + : procex(procex), name(name), flags(flags | CMD_PROCEX), type(type) {} + + inline CommandCost Execute(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text, uint32 binary_length) const { + if (this->flags & CMD_PROCEX) { + return this->procex(tile, flags, p1, p2, text, binary_length); + } else { + return this->proc(tile, flags, p1, p2, text); + } + } }; /**