I reverse engineered the function for cutting announcement lines to an appropriate length based on grid size, and found some issues with it.
(See comments on:
http://www.bay12games.com/dwarves/mantisbt/view.php?id=11813)
Equivalent code with testing is here:
#define crash_test false
#pragma warning(disable : 4996)
#include <cstring>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
//function at 0x140657290, df_47_05_win 64-bit
void parseAnnounce(string input, vector<char*> &out_vector, size_t max_line_length)
{ //out_vector will contain cstrings cut to max_line_length, unless would cut word
if (input.length() == 0 || max_line_length == 0) //max_line_length of 0 results in subscript error
return;
bool ignore_space = false;
string current_line = "";
char *cstring_ptr;
size_t iter = 0;
do
{
if (ignore_space)
{
if (input[iter] == ' ')
continue;
ignore_space = false;
}
if (input[iter] == '&') //escape character
{
iter++; //ignore the '&'
if (iter >= input.length())
break;
if (input[iter] == 'r') //"&r" starts new line
{
if (current_line.length() != 0)
{ //push current_line as cstring to out_vector
cstring_ptr = new char[current_line.length() + 1];
strcpy(cstring_ptr, current_line.c_str());
out_vector.push_back(cstring_ptr);
current_line = "";
}
//push cstring " \0" to out_vector
cstring_ptr = new char[2];
strcpy(cstring_ptr, " ");
out_vector.push_back(cstring_ptr);
continue; //don't add 'r' to current_line
}
else if (input[iter] != '&')
{ //not "&&", don't add character to current_line
continue;
}
}
current_line += input[iter];
if (current_line.length() > max_line_length)
{
size_t iter_backup = iter;
while (input[--iter] != ' ' && iter > 0); //roll back to most recent space in input, ignoring char just added
if ((iter_backup - iter) != current_line.length()) //false when most recent space at end of previous line?
{
size_t post_space_index = current_line.length() - (iter_backup - iter); //index of char after space in current_line (usually)
if (post_space_index <= current_line.length()) //can be false with ampersands in input and low max_line_length, resulting in underflow
{
current_line.erase(post_space_index); //end string after the space (incorrect with enough ampersands in input)
}
else
{ //end up here if at least max_line_length+1 unbroken chars since start of line, post_space_index underflow
current_line.append(post_space_index, '\0'); //insufficient fix, just causes length error
}
//push current_line as cstring to out_vector
cstring_ptr = new char[current_line.length() + 1];
strcpy(cstring_ptr, current_line.c_str());
out_vector.push_back(cstring_ptr);
ignore_space = true;
current_line = "";
}
else
{
iter = iter_backup; //keep building this line until we get a space
}
}
} while (++iter < input.length());
if (current_line.length() != 0)
{ //push current_line as cstring to out_vector
cstring_ptr = new char[current_line.length() + 1];
strcpy(cstring_ptr, current_line.c_str());
out_vector.push_back(cstring_ptr);
}
return;
}
int main()
{
int grid_x = 80; //init setting: min 80, max 255 (probably)
//"The <profession> strikes at the <profession> but the shot is blocked!"
string text = "The &&aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa& strikes at the &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& but the shot is blocked!"; //as appears in gamelog.txt
vector<char*> results;
cout << "\ngrid_x = " << grid_x << "\ntext = \"" << text;
cout << "\"\ncalling parseAnnounce(text, results, " << grid_x - 7 << ") ..." << endl;
parseAnnounce(text, results, grid_x-7); //the only calls to this function in DF use grid_x, grid_x-7, and grid_x-10
cout << "\nresults:" << endl;
for (auto i = results.begin(); i != results.end(); ++i)
{
cout << "\"" << *i << "\"" << endl;
}
//"The &aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
//"&&&&&&&&&&&&&&&&&&&but the shot is blocked!"
#if crash_test
cout << "\nClearing results vector ...\n" << endl;
results.clear();
grid_x = 16; //this is invalid, but lets us test with a shorter string
text = "&r123456789 and we've already crashed!";
cout << "grid_x = " << grid_x << "\ntext = \"" << text;
cout << "\"\ncalling parseAnnounce(text, results, " << grid_x - 7 << ") ..." << endl;
parseAnnounce(text, results, grid_x - 7); //results in string length error
cout << "\nresults:" << endl;
for (auto i = results.begin(); i != results.end(); ++i)
{
cout << "\"" << *i << "\"" << endl;
}
#endif
cout << "\nEnd of program." << endl;
return 0;
}
Is there an easy way to fix the issues with post_space_index, or would the whole function need to be rewritten?