1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-22 17:49:43 +01:00

Make commands, sb, and hooklist not use separate length-trackers

This is a refactor that turns the script-related arrays `ed.sb`, and
`ed.hooklist` into C++ vectors (`script.commands` was already a vector, it was
just misused). The code handling these vectors now looks more like idiomatic
C++ than sloppily-pasted pseudo-ActionScript. This removes the variables
`script.scriptlength`, `ed.sblength`, and `ed.numhooks`, too.

This reduces the amount of code needed to e.g. simply remove something from
any of these vectors. Previously the code had to manually shift the rest of
the elements down one-by-one, and doing it manually is definitely error-prone
and tedious.

But now we can just use fancy functions like `std::vector::erase()` and
`std::remove()` to do it all in one line!

Don't worry, I checked and `std::remove()` is in the C++ standard since at least
1998.

This patch makes it so the `commands` vector gets cleared when
`scriptclass::load()` is ran. Previously, the `commands` vector never actually
properly got cleared, so there could potentially be glitches that rely on the
game indexing past the bounds set by `scriptlength` but still in-bounds in the
eyes of C++, and people could potentially rely on such an exploit...

However, I checked, and I'm pretty sure that no such glitch previously existed
at all, because the only times the vector gets indexed are when `scriptlength`
is either being incremented after starting from 0 (`add()`) or when it's
underneath a `position < scriptlength` conditional.

Furthermore, I'm unaware of anyone who has actually found or used such an
exploit, and I've been in the custom level community for 6 years.

So I think it's fine.
This commit is contained in:
Misa 2020-02-20 09:43:52 -08:00 committed by Ethan Lee
parent 58fc42b638
commit 1be398319c
5 changed files with 43 additions and 99 deletions

View file

@ -11,12 +11,10 @@ scriptclass::scriptclass()
//Start SDL //Start SDL
//Init //Init
commands.resize(500);
words.resize(40); words.resize(40);
txt.resize(40); txt.resize(40);
position = 0; position = 0;
scriptlength = 0;
scriptdelay = 0; scriptdelay = 0;
running = false; running = false;
@ -72,7 +70,7 @@ void scriptclass::run( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map,
{ {
while(running && scriptdelay<=0 && !game.pausescript) while(running && scriptdelay<=0 && !game.pausescript)
{ {
if (position < scriptlength) if (position < (int) commands.size())
{ {
//Let's split or command in an array of words //Let's split or command in an array of words
tokenize(commands[position]); tokenize(commands[position]);
@ -3596,7 +3594,7 @@ void scriptclass::hardreset( KeyPoll& key, Graphics& dwgfx, Game& game,mapclass&
//Script Stuff //Script Stuff
position = 0; position = 0;
scriptlength = 0; commands.clear();
scriptdelay = 0; scriptdelay = 0;
scriptname = "null"; scriptname = "null";
running = false; running = false;

View file

@ -23,8 +23,7 @@ public:
void inline add(std::string t) void inline add(std::string t)
{ {
commands[scriptlength] = t; commands.push_back(t);
scriptlength++;
} }
void clearcustom(); void clearcustom();
@ -51,7 +50,7 @@ public:
std::vector<std::string> words; std::vector<std::string> words;
std::vector<std::string> txt; std::vector<std::string> txt;
std::string scriptname; std::string scriptname;
int position, scriptlength; int position;
int looppoint, loopcount; int looppoint, loopcount;
int scriptdelay; int scriptdelay;

View file

@ -11,7 +11,7 @@ void scriptclass::load(std::string t)
{ {
//loads script name t into the array //loads script name t into the array
position = 0; position = 0;
scriptlength=0; commands.clear();
running = true; running = true;
int maxlength = (std::min(int(t.length()),7)); int maxlength = (std::min(int(t.length()),7));

View file

@ -324,25 +324,11 @@ void editorclass::reset()
} }
} }
if(numhooks>0) hooklist.clear();
{
for(int i=0; i<numhooks; i++)
{
removehook(hooklist[i]);
}
}
for (int i = 0; i < 500; i++) sb.clear();
{
sb[i]="";
}
for (int i = 0; i < 500; i++)
{
hooklist[i]="";
}
clearscriptbuffer(); clearscriptbuffer();
sblength=1;
sbx=0; sbx=0;
sby=0; sby=0;
pagey=0; pagey=0;
@ -353,7 +339,6 @@ void editorclass::reset()
hookmenupage=0; hookmenupage=0;
hookmenu=0; hookmenu=0;
numhooks=0;
script.customscript.clear(); script.customscript.clear();
returneditoralpha = 0; returneditoralpha = 0;
@ -370,7 +355,7 @@ void editorclass::weirdloadthing(std::string t)
void editorclass::gethooks() void editorclass::gethooks()
{ {
//Scan through the script and create a hooks list based on it //Scan through the script and create a hooks list based on it
numhooks=0; hooklist.clear();
std::string tstring; std::string tstring;
std::string tstring2; std::string tstring2;
for(size_t i=0; i<script.customscript.size(); i++) for(size_t i=0; i<script.customscript.size(); i++)
@ -392,8 +377,7 @@ void editorclass::gethooks()
{ {
tstring2+=tstring[j]; tstring2+=tstring[j];
} }
hooklist[numhooks]=tstring2; hooklist.push_back(tstring2);
numhooks++;
} }
} }
} }
@ -427,12 +411,15 @@ void editorclass::loadhookineditor(std::string t)
else else
{ {
//load in this line //load in this line
sb[sblength-1]=script.customscript[i]; sb.push_back(script.customscript[i]);
sblength++;
} }
} }
} }
if(sblength>1) sblength--; if(sb.empty())
{
//Always have one line or we'll have problems
sb.resize(1);
}
} }
void editorclass::addhooktoscript(std::string t) void editorclass::addhooktoscript(std::string t)
@ -440,13 +427,10 @@ void editorclass::addhooktoscript(std::string t)
//Adds hook+the scriptbuffer to the end of the scriptclass //Adds hook+the scriptbuffer to the end of the scriptclass
removehookfromscript(t); removehookfromscript(t);
script.customscript.push_back(t+":"); script.customscript.push_back(t+":");
if(sblength>=1) for(size_t i=0; i<sb.size(); i++)
{
for(int i=0; i<sblength; i++)
{ {
script.customscript.push_back(sb[i]); script.customscript.push_back(sb[i]);
} }
}
} }
void editorclass::removehookfromscript(std::string t) void editorclass::removehookfromscript(std::string t)
@ -500,35 +484,22 @@ void editorclass::removehookfromscript(std::string t)
void editorclass::removehook(std::string t) void editorclass::removehook(std::string t)
{ {
//Check the hooklist for the hook t. If it's there, remove it from here and the script //Check the hooklist for the hook t. If it's there, remove it from here and the script
for(int i=0; i<numhooks; i++)
{
if(hooklist[i]==t)
{
removehookfromscript(t); removehookfromscript(t);
for(int j=i; j<numhooks; j++) hooklist.erase(std::remove(hooklist.begin(), hooklist.end(), t), hooklist.end());
{
hooklist[j]=hooklist[j+1];
}
hooklist[numhooks]="";
numhooks--;
i--;
}
}
} }
void editorclass::addhook(std::string t) void editorclass::addhook(std::string t)
{ {
//Add an empty function to the list in both editor and script //Add an empty function to the list in both editor and script
removehook(t); removehook(t);
hooklist[numhooks]=t; hooklist.push_back(t);
numhooks++;
addhooktoscript(t); addhooktoscript(t);
} }
bool editorclass::checkhook(std::string t) bool editorclass::checkhook(std::string t)
{ {
//returns true if hook t already is in the list //returns true if hook t already is in the list
for(int i=0; i<numhooks; i++) for(size_t i=0; i<hooklist.size(); i++)
{ {
if(hooklist[i]==t) return true; if(hooklist[i]==t) return true;
} }
@ -538,43 +509,22 @@ bool editorclass::checkhook(std::string t)
void editorclass::clearscriptbuffer() void editorclass::clearscriptbuffer()
{ {
for(int i=0; i<sblength+1; i++) sb.clear();
{
sb[i]="";
}
sblength=1;
} }
void editorclass::removeline(int t) void editorclass::removeline(int t)
{ {
//Remove line t from the script //Remove line t from the script
if(sblength>1) if((int)sb.size()>1)
{ {
if(sblength==t) sb.erase(sb.begin() + t);
{
sblength--;
}
else
{
for(int i=t; i<sblength; i++)
{
sb[i]=sb[i+1];
}
sb[sblength]="";
sblength--;
}
} }
} }
void editorclass::insertline(int t) void editorclass::insertline(int t)
{ {
//insert a blank line into script at line t //insert a blank line into script at line t
for(int i=sblength; i>=t; i--) sb.insert(sb.begin() + t, "");
{
sb[i+1]=sb[i];
}
sb[t]="";
sblength++;
} }
void editorclass::loadlevel( int rxi, int ryi ) void editorclass::loadlevel( int rxi, int ryi )
@ -2912,21 +2862,21 @@ void editorrender( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, ent
dwgfx.Print(16,44,"PRESS ESC TO RETURN TO MENU", 123, 111, 218, true); dwgfx.Print(16,44,"PRESS ESC TO RETURN TO MENU", 123, 111, 218, true);
//dwgfx.Print(16,60,"READY.", 123, 111, 218, false); //dwgfx.Print(16,60,"READY.", 123, 111, 218, false);
if(ed.numhooks>0) if(!ed.hooklist.empty())
{ {
for(int i=0; i<9; i++) for(int i=0; i<9; i++)
{ {
if(ed.hookmenupage+i<ed.numhooks) if(ed.hookmenupage+i<(int)ed.hooklist.size())
{ {
if(ed.hookmenupage+i==ed.hookmenu) if(ed.hookmenupage+i==ed.hookmenu)
{ {
std::string tstring="> " + ed.hooklist[(ed.numhooks-1)-(ed.hookmenupage+i)] + " <"; std::string tstring="> " + ed.hooklist[(ed.hooklist.size()-1)-(ed.hookmenupage+i)] + " <";
std::transform(tstring.begin(), tstring.end(),tstring.begin(), ::toupper); std::transform(tstring.begin(), tstring.end(),tstring.begin(), ::toupper);
dwgfx.Print(16,68+(i*16),tstring,123, 111, 218, true); dwgfx.Print(16,68+(i*16),tstring,123, 111, 218, true);
} }
else else
{ {
dwgfx.Print(16,68+(i*16),ed.hooklist[(ed.numhooks-1)-(ed.hookmenupage+i)],123, 111, 218, true); dwgfx.Print(16,68+(i*16),ed.hooklist[(ed.hooklist.size()-1)-(ed.hookmenupage+i)],123, 111, 218, true);
} }
} }
} }
@ -2945,7 +2895,7 @@ void editorrender( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, ent
//Draw text //Draw text
for(int i=0; i<25; i++) for(int i=0; i<25; i++)
{ {
if(i+ed.pagey<500) if(i+ed.pagey<(int)ed.sb.size())
{ {
dwgfx.Print(16,20+(i*8),ed.sb[i+ed.pagey], 123, 111, 218, false); dwgfx.Print(16,20+(i*8),ed.sb[i+ed.pagey], 123, 111, 218, false);
} }
@ -3526,15 +3476,15 @@ void editorrender( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, ent
} }
/* /*
for(int i=0; i<script.customscript.size(); i++){ for(size_t i=0; i<script.customscript.size(); i++){
dwgfx.Print(0,i*8,script.customscript[i],255,255,255); dwgfx.Print(0,i*8,script.customscript[i],255,255,255);
} }
dwgfx.Print(0,8*script.customscript.size(),help.String(script.customscript.size()),255,255,255); dwgfx.Print(0,8*script.customscript.size(),help.String(script.customscript.size()),255,255,255);
for(int i=0; i<ed.numhooks; i++){ for(size_t i=0; i<ed.hooklist.size(); i++){
dwgfx.Print(260,i*8,ed.hooklist[i],255,255,255); dwgfx.Print(260,i*8,ed.hooklist[i],255,255,255);
} }
dwgfx.Print(260,8*ed.numhooks,help.String(ed.numhooks),255,255,255); dwgfx.Print(260,8*ed.hooklist.size(),help.String(ed.hooklist.size()),255,255,255);
*/ */
if(ed.notedelay>0) if(ed.notedelay>0)
@ -3718,9 +3668,9 @@ void editorinput( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, enti
ed.hookmenu++; ed.hookmenu++;
} }
if(ed.hookmenu>=ed.numhooks) if(ed.hookmenu>=(int)ed.hooklist.size())
{ {
ed.hookmenu=ed.numhooks-1; ed.hookmenu=ed.hooklist.size()-1;
} }
if(ed.hookmenu<0) ed.hookmenu=0; if(ed.hookmenu<0) ed.hookmenu=0;
@ -3740,7 +3690,7 @@ void editorinput( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, enti
{ {
ed.deletekeyheld=1; ed.deletekeyheld=1;
music.playef(2); music.playef(2);
ed.removehook(ed.hooklist[(ed.numhooks-1)-ed.hookmenu]); ed.removehook(ed.hooklist[(ed.hooklist.size()-1)-ed.hookmenu]);
} }
if (!game.press_action && !game.press_left && !game.press_right if (!game.press_action && !game.press_left && !game.press_right
@ -3752,15 +3702,15 @@ void editorinput( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, enti
{ {
game.jumpheld = true; game.jumpheld = true;
} }
if ((game.press_action || game.press_map) && ed.numhooks>0) if ((game.press_action || game.press_map) && !ed.hooklist.empty())
{ {
game.mapheld=true; game.mapheld=true;
ed.scripthelppage=1; ed.scripthelppage=1;
key.keybuffer=""; key.keybuffer="";
ed.sbscript=ed.hooklist[(ed.numhooks-1)-ed.hookmenu]; ed.sbscript=ed.hooklist[(ed.hooklist.size()-1)-ed.hookmenu];
ed.loadhookineditor(ed.sbscript); ed.loadhookineditor(ed.sbscript);
ed.sby=ed.sblength-1; ed.sby=ed.sb.size()-1;
ed.pagey=0; ed.pagey=0;
while(ed.sby>=20) while(ed.sby>=20)
{ {
@ -3813,7 +3763,7 @@ void editorinput( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, enti
if(key.keymap[SDLK_DOWN] && ed.keydelay<=0) if(key.keymap[SDLK_DOWN] && ed.keydelay<=0)
{ {
ed.keydelay=6; ed.keydelay=6;
if(ed.sby+ed.pagey<ed.sblength-1) if(ed.sby+ed.pagey<(int)ed.sb.size()-1)
{ {
ed.sby++; ed.sby++;
if(ed.sby>=20) if(ed.sby>=20)
@ -3862,7 +3812,7 @@ void editorinput( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, enti
{ {
game.mapheld=true; game.mapheld=true;
//Continue to next line //Continue to next line
if(ed.sby+ed.pagey>=ed.sblength) //we're on the last line if(ed.sby+ed.pagey>=(int)ed.sb.size()) //we're on the last line
{ {
ed.sby++; ed.sby++;
if(ed.sby>=20) if(ed.sby>=20)
@ -3870,7 +3820,6 @@ void editorinput( KeyPoll& key, Graphics& dwgfx, Game& game, mapclass& map, enti
ed.pagey++; ed.pagey++;
ed.sby--; ed.sby--;
} }
if(ed.sby+ed.pagey>=ed.sblength) ed.sblength=ed.sby+ed.pagey;
key.keybuffer=ed.sb[ed.pagey+ed.sby]; key.keybuffer=ed.sb[ed.pagey+ed.sby];
ed.sbx = utf8::unchecked::distance(ed.sb[ed.pagey+ed.sby].begin(), ed.sb[ed.pagey+ed.sby].end()); ed.sbx = utf8::unchecked::distance(ed.sb[ed.pagey+ed.sby].begin(), ed.sb[ed.pagey+ed.sby].end());
} }

View file

@ -207,9 +207,8 @@ class editorclass{
bool scripteditmod; bool scripteditmod;
int scripthelppage, scripthelppagedelay; int scripthelppage, scripthelppagedelay;
std::string sb[500]; std::vector<std::string> sb;
std::string sbscript; std::string sbscript;
int sblength;
int sbx, sby; int sbx, sby;
int pagey; int pagey;
@ -226,8 +225,7 @@ class editorclass{
void clearscriptbuffer(); void clearscriptbuffer();
void gethooks(); void gethooks();
bool checkhook(std::string t); bool checkhook(std::string t);
std::string hooklist[500]; std::vector<std::string> hooklist;
int numhooks;
int hookmenupage, hookmenu; int hookmenupage, hookmenu;