Initial support for multi-item instructions.

This involves various changes to instruction modification actions,
and the GUI code.
This commit is contained in:
Jonathan G Rennison
2015-07-28 00:56:19 +01:00
parent 2c5bfc5486
commit 116bd41101
3 changed files with 148 additions and 23 deletions

View File

@@ -395,6 +395,14 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
} }
HandleCondition(condstack, condflags, true); HandleCondition(condstack, condflags, true);
} }
} else {
// check multi-word instructions
if (IsTraceRestrictDoubleItem(item)) {
i++;
if (i >= size) {
return_cmd_error(STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE); // instruction ran off end
}
}
} }
} }
if(!condstack.empty()) { if(!condstack.empty()) {
@@ -403,6 +411,35 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
return CommandCost(); return CommandCost();
} }
/**
* Convert an instruction index into an item array index
*/
size_t TraceRestrictProgram::InstructionOffsetToArrayOffset(const std::vector<TraceRestrictItem> &items, size_t offset)
{
size_t output_offset = 0;
size_t size = items.size();
for (size_t i = 0; i < offset && output_offset < size; i++, output_offset++) {
if (IsTraceRestrictDoubleItem(items[output_offset])) {
output_offset++;
}
}
return output_offset;
}
/**
* Convert an item array index into an instruction index
*/
size_t TraceRestrictProgram::ArrayOffsetToInstructionOffset(const std::vector<TraceRestrictItem> &items, size_t offset)
{
size_t output_offset = 0;
for (size_t i = 0; i < offset; i++, output_offset++) {
if (IsTraceRestrictDoubleItem(items[i])) {
i++;
}
}
return output_offset;
}
/** /**
* Set the value and aux field of @p item, as per the value type in @p value_type * Set the value and aux field of @p item, as per the value type in @p value_type
*/ */
@@ -595,6 +632,18 @@ static CommandCost TraceRestrictCheckTileIsUsable(TileIndex tile, Track track)
return CommandCost(); return CommandCost();
} }
/**
* Returns an appropriate default value for the second item of a dual-item instruction
* @p item is the first item of the instruction
*/
static uint32 GetDualInstructionInitialValue(TraceRestrictItem item)
{
switch (GetTraceRestrictType(item)) {
default:
NOT_REACHED();
}
}
/** /**
* The main command for editing a signal tracerestrict program. * The main command for editing a signal tracerestrict program.
* @param tile The tile which contains the signal. * @param tile The tile which contains the signal.
@@ -641,28 +690,46 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u
switch (type) { switch (type) {
case TRDCT_INSERT_ITEM: case TRDCT_INSERT_ITEM:
items.insert(items.begin() + offset, item); items.insert(TraceRestrictProgram::InstructionAt(items, offset), item);
if (IsTraceRestrictConditional(item) && if (IsTraceRestrictConditional(item) &&
GetTraceRestrictCondFlags(item) == 0 && GetTraceRestrictCondFlags(item) == 0 &&
GetTraceRestrictType(item) != TRIT_COND_ENDIF) { GetTraceRestrictType(item) != TRIT_COND_ENDIF) {
// this is an opening if block, insert a corresponding end if // this is an opening if block, insert a corresponding end if
TraceRestrictItem endif_item = 0; TraceRestrictItem endif_item = 0;
SetTraceRestrictType(endif_item, TRIT_COND_ENDIF); SetTraceRestrictType(endif_item, TRIT_COND_ENDIF);
items.insert(items.begin() + offset + 1, endif_item); items.insert(TraceRestrictProgram::InstructionAt(items, offset) + 1, endif_item);
} else if (IsTraceRestrictDoubleItem(item)) {
items.insert(TraceRestrictProgram::InstructionAt(items, offset) + 1, GetDualInstructionInitialValue(item));
} }
break; break;
case TRDCT_MODIFY_ITEM: { case TRDCT_MODIFY_ITEM: {
TraceRestrictItem old_item = items[offset]; std::vector<TraceRestrictItem>::iterator old_item = TraceRestrictProgram::InstructionAt(items, offset);
if (IsTraceRestrictConditional(old_item) != IsTraceRestrictConditional(item)) { if (IsTraceRestrictConditional(*old_item) != IsTraceRestrictConditional(item)) {
return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY); return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY);
} }
items[offset] = item; bool old_is_dual = IsTraceRestrictDoubleItem(*old_item);
bool new_is_dual = IsTraceRestrictDoubleItem(item);
*old_item = item;
if (old_is_dual && !new_is_dual) {
items.erase(old_item + 1);
} else if (!old_is_dual && new_is_dual) {
items.insert(old_item + 1, GetDualInstructionInitialValue(item));
}
break;
}
case TRDCT_MODIFY_DUAL_ITEM: {
std::vector<TraceRestrictItem>::iterator old_item = TraceRestrictProgram::InstructionAt(items, offset);
if (!IsTraceRestrictDoubleItem(*old_item)) {
return CMD_ERROR;
}
*(old_item + 1) = p2;
break; break;
} }
case TRDCT_REMOVE_ITEM: { case TRDCT_REMOVE_ITEM: {
TraceRestrictItem old_item = items[offset]; TraceRestrictItem old_item = *TraceRestrictProgram::InstructionAt(items, offset);
if (IsTraceRestrictConditional(old_item)) { if (IsTraceRestrictConditional(old_item)) {
bool remove_whole_block = false; bool remove_whole_block = false;
if (GetTraceRestrictCondFlags(old_item) == 0) { if (GetTraceRestrictCondFlags(old_item) == 0) {
@@ -676,7 +743,7 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u
} }
uint32 recursion_depth = 1; uint32 recursion_depth = 1;
std::vector<TraceRestrictItem>::iterator remove_start = items.begin() + offset; std::vector<TraceRestrictItem>::iterator remove_start = TraceRestrictProgram::InstructionAt(items, offset);
std::vector<TraceRestrictItem>::iterator remove_end = remove_start + 1; std::vector<TraceRestrictItem>::iterator remove_end = remove_start + 1;
// iterate until matching end block found // iterate until matching end block found
@@ -709,12 +776,22 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u
break; break;
} }
} }
} else if (IsTraceRestrictDoubleItem(current_item)) {
// this is a double-item, jump over the next item as well
++remove_end;
} }
} }
if (recursion_depth != 0) return CMD_ERROR; // ran off the end if (recursion_depth != 0) return CMD_ERROR; // ran off the end
items.erase(remove_start, remove_end); items.erase(remove_start, remove_end);
} else { } else {
items.erase(items.begin() + offset); std::vector<TraceRestrictItem>::iterator remove_start = TraceRestrictProgram::InstructionAt(items, offset);
std::vector<TraceRestrictItem>::iterator remove_end = remove_start + 1;
if (IsTraceRestrictDoubleItem(old_item)) {
// this is a double-item, remove the next item as well
++remove_end;
}
items.erase(remove_start, remove_end);
} }
break; break;
} }

View File

@@ -216,6 +216,46 @@ struct TraceRestrictProgram : TraceRestrictProgramPool::PoolItem<&_tracerestrict
static CommandCost Validate(const std::vector<TraceRestrictItem> &items); static CommandCost Validate(const std::vector<TraceRestrictItem> &items);
static size_t InstructionOffsetToArrayOffset(const std::vector<TraceRestrictItem> &items, size_t offset);
static size_t ArrayOffsetToInstructionOffset(const std::vector<TraceRestrictItem> &items, size_t offset);
/** Call InstructionOffsetToArrayOffset on current program instruction list */
size_t InstructionOffsetToArrayOffset(size_t offset) const
{
return TraceRestrictProgram::InstructionOffsetToArrayOffset(this->items, offset);
}
/** Call ArrayOffsetToInstructionOffset on current program instruction list */
size_t ArrayOffsetToInstructionOffset(size_t offset) const
{
return TraceRestrictProgram::ArrayOffsetToInstructionOffset(this->items, offset);
}
/** Get number of instructions in @p items */
static size_t GetInstructionCount(const std::vector<TraceRestrictItem> &items)
{
return ArrayOffsetToInstructionOffset(items, items.size());
}
/** Call GetInstructionCount on current program instruction list */
size_t GetInstructionCount() const
{
return TraceRestrictProgram::GetInstructionCount(this->items);
}
/** Get an iterator to the instruction at a given @p instruction_offset in @p items */
static std::vector<TraceRestrictItem>::iterator InstructionAt(std::vector<TraceRestrictItem> &items, size_t instruction_offset)
{
return items.begin() + TraceRestrictProgram::InstructionOffsetToArrayOffset(items, instruction_offset);
}
/** Get a const_iterator to the instruction at a given @p instruction_offset in @p items */
static std::vector<TraceRestrictItem>::const_iterator InstructionAt(const std::vector<TraceRestrictItem> &items, size_t instruction_offset)
{
return items.begin() + TraceRestrictProgram::InstructionOffsetToArrayOffset(items, instruction_offset);
}
/** /**
* Call validation function on current program instruction list * Call validation function on current program instruction list
*/ */
@@ -294,6 +334,12 @@ static inline bool IsTraceRestrictConditional(TraceRestrictItem item)
return IsTraceRestrictTypeConditional(GetTraceRestrictType(item)); return IsTraceRestrictTypeConditional(GetTraceRestrictType(item));
} }
/** Is TraceRestrictItem a double-item type? */
static inline bool IsTraceRestrictDoubleItem(TraceRestrictItem item)
{
return false;
}
/** /**
* Categorisation of what is allowed in the TraceRestrictItem condition op field * Categorisation of what is allowed in the TraceRestrictItem condition op field
* see TraceRestrictTypePropertySet * see TraceRestrictTypePropertySet
@@ -432,14 +478,15 @@ static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIn
* Enumeration for command action type field, indicates what command to do * Enumeration for command action type field, indicates what command to do
*/ */
enum TraceRestrictDoCommandType { enum TraceRestrictDoCommandType {
TRDCT_INSERT_ITEM = 0, ///< insert new instruction before offset field as given value TRDCT_INSERT_ITEM, ///< insert new instruction before offset field as given value
TRDCT_MODIFY_ITEM = 1, ///< modify instruction at offset field to given value TRDCT_MODIFY_ITEM, ///< modify instruction at offset field to given value
TRDCT_REMOVE_ITEM = 2, ///< remove instruction at offset field TRDCT_MODIFY_DUAL_ITEM, ///< modify second item of dual-part instruction at offset field to given value
TRDCT_REMOVE_ITEM, ///< remove instruction at offset field
TRDCT_PROG_COPY = 3, ///< copy program operation. Do not re-order this with respect to other values TRDCT_PROG_COPY, ///< copy program operation. Do not re-order this with respect to other values
TRDCT_PROG_SHARE = 4, ///< share program operation TRDCT_PROG_SHARE, ///< share program operation
TRDCT_PROG_UNSHARE = 5, ///< unshare program (copy as a new program) TRDCT_PROG_UNSHARE, ///< unshare program (copy as a new program)
TRDCT_PROG_RESET = 6, ///< reset program state of signal TRDCT_PROG_RESET, ///< reset program state of signal
}; };
void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg); void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg);

View File

@@ -1109,7 +1109,7 @@ private:
int GetItemCount(const TraceRestrictProgram *prog) const int GetItemCount(const TraceRestrictProgram *prog) const
{ {
if (prog) { if (prog) {
return 2 + prog->items.size(); return 2 + prog->GetInstructionCount();
} else { } else {
return 2; return 2;
} }
@@ -1141,17 +1141,17 @@ private:
} }
if (prog) { if (prog) {
const std::vector<TraceRestrictItem> &items = prog->items; size_t instruction_count = prog->GetInstructionCount();
if (static_cast<size_t>(index) == items.size() + 1) { if (static_cast<size_t>(index) == instruction_count + 1) {
return MakeSpecialItem(TRNTSV_END); return MakeSpecialItem(TRNTSV_END);
} }
if (static_cast<size_t>(index) > items.size() + 1) { if (static_cast<size_t>(index) > instruction_count + 1) {
return 0; return 0;
} }
return items[index - 1]; return prog->items[prog->InstructionOffsetToArrayOffset(index - 1)];
} else { } else {
// No program defined, this is equivalent to an empty program // No program defined, this is equivalent to an empty program
if (index == 1) { if (index == 1) {
@@ -1460,12 +1460,13 @@ private:
std::vector<TraceRestrictItem> items = prog->items; // copy std::vector<TraceRestrictItem> items = prog->items; // copy
if (offset >= (items.size() + (replace ? 0 : 1))) return false; // off the end of the program if (offset >= (TraceRestrictProgram::GetInstructionCount(items) + (replace ? 0 : 1))) return false; // off the end of the program
uint array_offset = TraceRestrictProgram::InstructionOffsetToArrayOffset(items, offset);
if (replace) { if (replace) {
items[offset] = item; items[array_offset] = item;
} else { } else {
items.insert(items.begin() + offset, item); items.insert(items.begin() + array_offset, item);
} }
return TraceRestrictProgram::Validate(items).Succeeded(); return TraceRestrictProgram::Validate(items).Succeeded();