This file is indexed.

/usr/share/fceux/luaScripts/Luabot.lua is in fceux 2.2.2+dfsg0-1build1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 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
-- BeeBee, LuaBot Frontend v1.07
-- qFox, 2 August 2008

-- we need iup, so include it here (also takes care of cleaning up dialog when script exits)
require 'auxlib';

local botVersion = 1; -- check this version when saving/loading. this will change whenever the botsave-file changes.

function createTextareaTab(reftable, tmptable, token, tab, fun, val) -- specific one, at that :)
	reftable[token] = iup.multiline{title="Contents",expand="YES", border="YES" }; -- ,value=val};
	tmptable[token] = iup.vbox{iup.label{title="function "..fun.."()\n  local result = no;"},reftable[token],iup.label{title="  return result;\nend;"}};
	tmptable[token].tabtitle = tab;
end;
function createTextareaTab2(reftable, tmptable, token, fun, arg) -- specific one, at that :) this one generates no return values
	reftable[token] = iup.multiline{title="Contents",expand="YES", border="YES" }; --,value=fun};
	if (arg) then
		tmptable[token] = iup.vbox{iup.label{title="function "..fun.."(wasOk) -- wasOk (boolean) is true when the attempt was ok\n"},reftable[token],iup.label{title="end;"}};
	else
		tmptable[token] = iup.vbox{iup.label{title="function "..fun.."()\n"},reftable[token],iup.label{title="end;"}};
	end;
	tmptable[token].tabtitle = fun;
end;

function createGUI(n)
	-- this table will keep the references for easy and fast lookup. <STRID, itemobj>
	local reftable = {};
	-- this table we wont keep, it holds references to (mostly) cosmetic elements of the dialog
	local tmptable = {};

	-- ok, dont be intimidated by the next eight blocks of code. they basically all say the same!
	-- every line creates an element for the gui and sets it up. every block is a tabbed pane and
	-- they are all put into another tabbed pane themselves. all references are put into a table
	-- paired with the tokens in the basicbot framework. this allows us to easily walk through
	-- all the pairs of <tokens,references> and replace them in the file, uppon writing.
	
	reftable.ROMNAME = iup.text{title="rom name", size="300x", value="something_or_the_other.rom"};
	tmptable.ROMNAME = iup.hbox{iup.label{title="Rom name: ", size="50x"}, reftable.ROMNAME, iup.fill{}};
	reftable.COMMENT = iup.text{title="comment", size="300x",value="a botscript for some game"};
	tmptable.COMMENT = iup.hbox{iup.label{title="Comment: ", size="50x"}, reftable.COMMENT, iup.fill{}};
	reftable.VERSION = iup.text{title="version", size="70x",value="1.00"};
	tmptable.VERSION = iup.hbox{iup.label{title="Version: ", size="50x"}, reftable.VERSION, iup.fill{}};
	tmptable.SAVE = iup.button{title="Save contents"};
	-- the callback is set after the dialog is created. we need the references to all controls for saving to work :)
	tmptable.LOAD = iup.button{title="Load contents"};
	tmptable.WRITE = iup.button{title="Write bot script"};
	general = iup.vbox{tmptable.ROMNAME,tmptable.COMMENT,tmptable.VERSION,iup.fill{size="5x",},tmptable.SAVE,iup.fill{size="5x",},tmptable.LOAD,iup.fill{size="5x",},tmptable.WRITE,iup.fill{}};
	general.tabtitle = "General";

	createTextareaTab(reftable, tmptable, "bA1",    "A",      "isPressedA1",      "a1");
	createTextareaTab(reftable, tmptable, "bB1",    "B",      "isPressedB1",      "b1");
	createTextareaTab(reftable, tmptable, "START1", "Start",  "isPressedStart1",  "start1");
	createTextareaTab(reftable, tmptable, "SELECT1","Select", "isPressedSelect1", "select1");
	createTextareaTab(reftable, tmptable, "UP1",    "Up",     "isPressedUp1",     "up1");
	createTextareaTab(reftable, tmptable, "DOWN1",  "Down",   "isPressedDown1",   "down1");
	createTextareaTab(reftable, tmptable, "LEFT1",  "Left",   "isPressedLeft1",   "left1");
	createTextareaTab(reftable, tmptable, "RIGHT1", "Right",  "isPressedRight1",  "right1");
	tabs1 = iup.vbox{iup.tabs{tmptable.bA1,tmptable.bB1,tmptable.START1,tmptable.SELECT1,tmptable.UP1,tmptable.DOWN1,tmptable.LEFT1,tmptable.RIGHT1}};
	tabs1.tabtitle = "Player 1";

	createTextareaTab(reftable, tmptable, "bA2",    "A",      "isPressedA2",      "a2");
	createTextareaTab(reftable, tmptable, "bB2",    "B",      "isPressedB2",      "b2");
	createTextareaTab(reftable, tmptable, "START2", "Start",  "isPressedStart2",  "start2");
	createTextareaTab(reftable, tmptable, "SELECT2","Select", "isPressedSelect2", "select2");
	createTextareaTab(reftable, tmptable, "UP2",    "Up",     "isPressedUp2",     "up2");
	createTextareaTab(reftable, tmptable, "DOWN2",  "Down",   "isPressedDown2",   "down2");
	createTextareaTab(reftable, tmptable, "LEFT2",  "Left",   "isPressedLeft2",   "left2");
	createTextareaTab(reftable, tmptable, "RIGHT2", "Right",  "isPressedRight2",  "right2");
	tabs2 = iup.vbox{iup.tabs{tmptable.bA2,tmptable.bB2,tmptable.START2,tmptable.SELECT2,tmptable.UP2,tmptable.DOWN2,tmptable.LEFT2,tmptable.RIGHT2}};
	tabs2.tabtitle = "Player 2";

	createTextareaTab(reftable, tmptable, "bA3",    "A",      "isPressedA3",      "a3");
	createTextareaTab(reftable, tmptable, "bB3",    "B",      "isPressedB3",      "b3");
	createTextareaTab(reftable, tmptable, "START3", "Start",  "isPressedStart3",  "start3");
	createTextareaTab(reftable, tmptable, "SELECT3","Select", "isPressedSelect3", "select3");
	createTextareaTab(reftable, tmptable, "UP3",    "Up",     "isPressedUp3",     "up3");
	createTextareaTab(reftable, tmptable, "DOWN3",  "Down",   "isPressedDown3",   "down3");
	createTextareaTab(reftable, tmptable, "LEFT3",  "Left",   "isPressedLeft3",   "left3");
	createTextareaTab(reftable, tmptable, "RIGHT3", "Right",  "isPressedRight3",  "right3");
	tabs3 = iup.vbox{iup.tabs{tmptable.bA3,tmptable.bB3,tmptable.START3,tmptable.SELECT3,tmptable.UP3,tmptable.DOWN3,tmptable.LEFT3,tmptable.RIGHT3}};
	tabs3.tabtitle = "Player 3";

	createTextareaTab(reftable, tmptable, "bA4",    "A",      "isPressedA4",      "a4");
	createTextareaTab(reftable, tmptable, "bB4",    "B",      "isPressedB4",      "b4");
	createTextareaTab(reftable, tmptable, "START4", "Start",  "isPressedStart4",  "start4");
	createTextareaTab(reftable, tmptable, "SELECT4","Select", "isPressedSelect4", "select4");
	createTextareaTab(reftable, tmptable, "UP4",    "Up",     "isPressedUp4",     "up4");
	createTextareaTab(reftable, tmptable, "DOWN4",  "Down",   "isPressedDown4",   "down4");
	createTextareaTab(reftable, tmptable, "LEFT4",  "Left",   "isPressedLeft4",   "left4");
	createTextareaTab(reftable, tmptable, "RIGHT4", "Right",  "isPressedRight4",  "right4");
	tabs4 = iup.vbox{iup.tabs{tmptable.bA4,tmptable.bB4,tmptable.START4,tmptable.SELECT4,tmptable.UP4,tmptable.DOWN4,tmptable.LEFT4,tmptable.RIGHT4}};
	tabs4.tabtitle = "Player 4";

	createTextareaTab2(reftable, tmptable, "ONSTART",       "onStart",        false);
	createTextareaTab2(reftable, tmptable, "ONFINISH",      "onFinish",       false);
	createTextareaTab2(reftable, tmptable, "ONSEGMENTSTART","onSegmentStart", false);
	createTextareaTab2(reftable, tmptable, "ONSEGMENTEND",  "onSegmentEnd",   false);
	createTextareaTab2(reftable, tmptable, "ONATTEMPTSTART","onAttemptStart", false);
	createTextareaTab2(reftable, tmptable, "ONATTEMPTEND",  "onAttemptEnd",   true);
	createTextareaTab2(reftable, tmptable, "ONINPUTSTART",  "onInputStart",   false);
	createTextareaTab2(reftable, tmptable, "ONINPUTEND",    "onInputEnd",     false);
	tabs5 = iup.vbox{iup.tabs{tmptable.ONSTART, tmptable.ONFINISH, tmptable.ONSEGMENTSTART, tmptable.ONSEGMENTEND, tmptable.ONATTEMPTSTART, tmptable.ONATTEMPTEND, tmptable.ONINPUTSTART, tmptable.ONINPUTEND}};
	tabs5.tabtitle = "Events";
	
	createTextareaTab(reftable, tmptable, "SCORE",    "score",      "getScore",      "score");
	createTextareaTab(reftable, tmptable, "TIE1",    "tie1",      "getTie1",      "tie1");
	createTextareaTab(reftable, tmptable, "TIE2",    "tie2",      "getTie2",      "tie2");
	createTextareaTab(reftable, tmptable, "TIE3",    "tie3",      "getTie3",      "tie3");
	createTextareaTab(reftable, tmptable, "TIE4",    "tie4",      "getTie4",      "tie4");
	tabs6 = iup.vbox{iup.tabs{tmptable.SCORE,tmptable.TIE1,tmptable.TIE2,tmptable.TIE3,tmptable.TIE4}};
	tabs6.tabtitle = "Score";
	
	createTextareaTab(reftable, tmptable, "ISRUNEND",     "isRunEnd",     "isRunEnd",     "isRunEnd");
	createTextareaTab(reftable, tmptable, "MUSTROLLBACK", "mustRollBack", "mustRollBack", "mustRollBack");
	createTextareaTab(reftable, tmptable, "ISSEGMENTEND", "isSegmentEnd", "isSegmentEnd", "isSegmentEnd");
	createTextareaTab(reftable, tmptable, "ISATTEMPTEND", "isAttemptEnd", "isAttemptEnd", "isAttemptEnd");
	createTextareaTab(reftable, tmptable, "ISATTEMPTOK",  "isAttemptOk",  "isAttemptOk",  "isAttemptOk");
	tabs7 = iup.vbox{iup.tabs{tmptable.ISRUNEND,tmptable.MUSTROLLBACK,tmptable.ISSEGMENTEND,tmptable.ISATTEMPTEND,tmptable.ISATTEMPTOK}};
	tabs7.tabtitle = "Selection";
	
	playertabs = iup.tabs{general,tabs1,tabs2,tabs3,tabs4,tabs5,tabs6,tabs7,title};
	handles[n] = iup.dialog{playertabs, title="BeeBee; BasicBot Frontend", size="450x200"}
	handles[n]:showxy(iup.CENTER, iup.CENTER)

	-- now set the callback function for the save button. this will use all the references above.
	-- these remain ok in the anonymous function by something called "closures". this means that
	-- these variables, although local to the scope of the function, will remain their value in
	-- the anonymous function. hence we can refer to them and fetch their contents, even though
	-- you cant refer to them outside the context of the createGUI function.
	tmptable.WRITE.action = 
		function(self, n)
			local file = iup.filedlg{allownew="YES",dialogtype="SAVE",directory="./lua",showhidden="YES",title="Save botfile"};
			file:popup(iup.ANYWHERE,iup.ANYWHERE);

			if (file.value == "NULL") then
				iup.Message("An error occurred trying to save your settings");
				return;
			elseif (file.status == "-1") then 
			  iup.Message("IupFileDlg","Operation canceled");
			  return;
			end

			-- ok, file selected, if an error occurred or user canceled, the function already returned, so lets write the bot!

			-- get the framework first. we need it to find the relevant tokens
			local fh = assert(io.open("luabot_framework.lua","r"));
			local framework = fh:read("*a");
			fh:close();

			-- now replace all tokens by gui values
			-- this is where the reftable comes in very handy :p
			for token,obj in pairs(reftable) do
				local st00pid = (reftable[token].value or "");
				framework = string.gsub(framework, "-- "..token, st00pid, 1); -- if nothing was entered, obj.value returns nil (not ""), so we have to make that translation
			end;

			-- open the file, if old file, clear it
			if (file.status == "1") then 
				fh = assert(io.open(file.value,"wb"));
			else -- (file.status == "0")
				fh = assert(io.open(file.value,"w+b")); -- clear file contents
			end;
			
			-- write it
			fh:write(framework);
			
			-- close it (automatically flushed)
			fh:close();
			fh = nil;
			
			iup.Message ("Success", "Bot written to "..file.value.."!");
		end;
	tmptable.SAVE.action =
		function(self, n)
			local file = iup.filedlg{allownew="YES",dialogtype="SAVE",directory="./lua",showhidden="YES",title="Save botfile",extfilter="BasicBot (*.bot)|*.bot|All files (*.*)|*.*|"};
			file:popup(iup.ANYWHERE,iup.ANYWHERE);

			if (file.status == 1) then -- cancel
				return;
			end;
			
			-- open the file, if old file, clear it
			if (file.status == "1") then 
				fh = assert(io.open(file.value,"wb"));
			else -- (file.status == "0")
				fh = assert(io.open(file.value,"w+b")); -- clear file contents
			end;

			-- allow us to detect the botfile version (warn the user if it's different?)
			fh:write(botVersion.."\n");

			-- now replace all tokens by gui values
			-- this is where the reftable comes in very handy :p
			for token,obj in pairs(reftable) do
				print("------");
				print(token.." control -> "..tostring(obj));
				print(".value: "..tostring(obj.value));
				local st00pid = obj.value;
				if (not st00pid) then st00pid = ""; end;
				print(string.len(st00pid));
				fh:write(string.len(st00pid).."\n");
				if (string.len(st00pid) > 0) then fh:write(st00pid); end;
				fh:write("\n");
			end;
			
			fh:close();
			iup.Message ("Success", "Settings saved!");
		end;
		tmptable.LOAD.action =
			function (self, n)
				-- this function currently crashes fceux without notification
				-- possibly because offsets are badly calculated, but serves as an example now
				local file = iup.filedlg{allownew="NO",dialogtype="OPEN",directory="./lua",showhidden="YES",title="Save botfile",extfilter="BasicBot (*.bot)|*.bot|All files (*.*)|*.*|"};
				file:popup(iup.ANYWHERE,iup.ANYWHERE);
				if (file.status == 1) then -- cancel
					iup.Message ("Success", "Canceled by you...");
					return;
				end;
				local nellen = string.len("\n"); -- platform independent
				fh = assert(io.open(file.value,"r"));

				fh:read("*n"); -- version
				fh:read("*l"); -- return
				
				local len;
				local data;
				for token,crap in pairs(reftable) do
					len = fh:read("*n"); -- read line (length)
					if (not len) then 
						iup.Message ("Warning", "End of file reached too soon!");
						break; 
					end; -- no more data... (should we erase the rest?)
					fh:read("*l"); -- return
					data = fh:read(len);
					if (not data) then 
						iup.Message ("Warning", "End of file reached too soon!");
						break; 
					end; -- no more data... (should we erase the rest?)
					reftable[token].value = data;
					fh:read("*l"); -- return
				end;
				iup.Message ("Success", "Settings loaded!");
			end;
end;

dialogs = dialogs + 1;
createGUI(dialogs);
while (true) do
	FCEU.frameadvance();
end;