Merge fixes in r10 maint branch
[jpcrr.git] / datafiles / luakernel
blob973ca1d19e72aa8618588e600b89967900469cbb
1 --
2 -- Copyright 2009-2010 Ilari Liusvaara
3 --
4 -- Licenced under GNU GPL v2.
5 --
7 -- Exported tables:
8 --      - args
9 --              Contains user-level arguments.
11 -- Exported functions:
12 --      - All lua standard functions in tables main, "coroutine", "string" and "table".
13 --      - modulus_split(number num, number...)
14 --              Returns quotents and quotent remainders of number divided by given dividers.
15 --              The number of results returned is equal to number of numbers passed. The
16 --              dividers must be in decreasing order and all dividers must be nonzero and
17 --              should be >1.
18 --      - assert(object ret, object err)
19 --              If ret is considered true, return ret. Otherwise raise err as error. Exception
20 --              if err is nil too, then ret is returned anyway.
21 --      - bit.none(number...)
22 --              Returns 48-bit number that has those bits set that are set in none of its
23 --              arguments.
24 --      - bit.any(number...)
25 --              Returns 48-bit number that has those bits set that are set in any of its
26 --              arguments.
27 --      - bit.parity(number...)
28 --              Returns 48-bit number that has those bits set that are set in even number
29 --              of its arguments.
30 --      - bit.any(number...)
31 --              Returns 48-bit number that has those bits set that are set in all of its
32 --              arguments.
33 --      - bit.lshift(number num, number shift)
34 --              Returns 48 rightmost bits of num shifted by shift places left. Going outside
35 --              shift range 0-47 produces unpredicatable results.
36 --      - bit.rshift(number num, number shift)
37 --              Returns 48 rightmost bits of num shifted by shift places right. Going outside
38 --              shift range 0-47 produces unpredicatable results.
39 --      - bit.arshift(number num, number shift)
40 --              Returns 48 rightmost bits of num shifted by shift places right and bit 47
41 --              value copied to all incoming bits. Going outside shift range 0-47 produces
42 --              unpredicatable results.
43 --      - bit.add(number...)
44 --              Returns sum of all passed numbers modulo 2^48.
45 --      - bit.addneg(number...)
46 --              Returns 0 minus sum of all passed numbers modulo 2^48.
47 --      - bit.addalt(number...)
48 --              Returns sum of odd arguments (first is odd) minus sum of even arguments modulo
49 --               2^48.
50 --      - bit.rol(number num, number shift)
51 --              Returns 48 rightmost bits of num rotated by shift places left. Going outside
52 --              shift range 0-47 produces unpredicatable results.
53 --      - bit.ror(number num, number shift)
54 --              Returns 48 rightmost bits of num rotated by shift places right. Going outside
55 --              shift range 0-47 produces unpredicatable results.
56 --      - bit.bswap2(number num)
57 --              Returns 16 rightmost bits of num byte-swapped.
58 --      - bit.bswap3(number num)
59 --              Returns 24 rightmost bits of num byte-swapped.
60 --      - bit.bswap4(number num)
61 --              Returns 32 rightmost bits of num byte-swapped.
62 --      - bit.bswap5(number num)
63 --              Returns 40 rightmost bits of num byte-swapped.
64 --      - bit.bswap6(number num)
65 --              Returns 48 rightmost bits of num byte-swapped.
66 --      - bit.signextend(number num, number bitnum)
67 --              Copy bitnum'th bit of num to all higher bits of num and return result. Going
68 --              outside bitnum range of 0-47 produces unpredictable results.
69 --      - bit.tosigned(number num, number bitnum)
70 --              Mask off bitnum'th bit and all higher bits. If bitnum'th bit of num was set,
71 --              perform 2s complement on number and negate the result.
72 --      - bit.tohex(number num)
73 --              Returns hexadecimal string representation of number.
74 --      - jpcrr.wait_event()
75 --              Waits for event. Returns event type and extra message (if present). Event
76 --              Types are "lock", "stop", "attach" and "message". Note that if you get event
77 --              of type "lock", you are now in frame hold.
78 --      - jpcrr.poll_event()
79 --              Same as wait_event(), but does not wait for event to occur (returns nil if there
80 --              is no event).
81 --      - jpcrr.pc_running()
82 --              Returns true if PC is running.
83 --      - jpcrr.clock_time()
84 --              Returns current time or nil if no PC.
85 --      - jpcrr.pc_connected()
86 --              Returns true if PC is connected.
87 --      - jpcrr.in_frame_hold()
88 --              Returns true if in frame hold, false otherwise.
89 --      - jpcrr.keypressed(number key)
90 --              Return true if key is pressed, else false.
91 --      - jpcrr.keypressed_edge(number key)
92 --              Return true if key is pressed at input edge, else false.
93 --      - jpcrr.release_vga()
94 --              Allow VGA to exit frame hold mode. Wait for frame hold first.
95 --      - jpcrr.vga_resolution()
96 --              Return VGA x and y resolutions. -1x-1 or 0x0 is returned if no valid resolution.
97 --              Should only be called during frame hold.
98 --      - jpcrr.frame_number()
99 --              Return current VGA frame number or nil if no PC present.
100 --      - jpcrr.shutdown_emulator()
101 --              Shutdown the entiere emulator immediately (graceful shutdown, PCRunner only).
102 --      - jpcrr.hud.left_gap(number flags, number gap)
103 --              Set left gap for HUD. If flags has bit 0 (1) set, draw on screen, if bit
104 --              1 (2) is set, dump to video dump.
105 --      - jpcrr.hud.right_gap(number flags, number gap)
106 --              Set right gap for HUD. If flags has bit 0 (1) set, draw on screen, if bit
107 --              1 (2) is set, dump to video dump.
108 --      - jpcrr.hud.top_gap(number flags, number gap)
109 --              Set top gap for HUD. If flags has bit 0 (1) set, draw on screen, if bit
110 --              1 (2) is set, dump to video dump.
111 --      - jpcrr.hud.bottom_gap(number flags, number gap)
112 --              Set bottom gap for HUD. If flags has bit 0 (1) set, draw on screen, if bit
113 --              1 (2) is set, dump to video dump.
114 --      - jpcrr.hud.white_solid_box(number flags, number x, number y, number w, number h)
115 --              Draw with solid opaque box.
116 --      - jpcrr.hud.box(number flags, number x, number y, number w, number h, number linethick,
117 --                      number lineRed, number lineGreen, number lineBlue, number lineAlpha,
118 --                      number fillRed, number fillGreen, number fillBlue, number fillAlpha)
119 --              Draw box with specified size, border line thickness, line color and fill color.
120 --      - jpcrr.hud.circle(number flags, number x, number y, number r, number linethick,
121 --                      number lineRed, number lineGreen, number lineBlue, number lineAlpha,
122 --                      number fillRed, number fillGreen, number fillBlue, number fillAlpha)
123 --              Draw circle with specified size, border line thickness, line color and fill color.
124 --      - jpcrr.hud.bitmap(number flags, number x, number y, string bmap,
125 --                      number fgRed, number fgGreen, number fgBlue, number fgAlpha,
126 --                      number bgRed, number bgGreen, number bgBlue, number bgAlpha)
127 --              Draw bitmap with specified foreground color and background color.
128 --      - jpcrr.hud.bitmap_binary(number flags, number x, number y, string bmap,
129 --                      number fgRed, number fgGreen, number fgBlue, number fgAlpha,
130 --                      number bgRed, number bgGreen, number bgBlue, number bgAlpha)
131 --              Draw binary bitmap with specified foreground color and background color.
132 --      - jpcrr.events.count()
133 --              Return current event count. Nil if no PC.
134 --      - jpcrr.events.current_sequence()
135 --              Return sequence number of next event (event sequence numbers are 0-based). Nil if
136 --              no PC, -1 if at end of movie.
137 --      - jpcrr.events.by_sequence(number seq)
138 --              Return array for specified event. Nil if no PC, empty array if not valid sequence
139 --              number. Otherwise, first element of array is timestamp (numeric), the second is
140 --              event class and rest are the arguments.
141 --      - jpcrr.joystick_state()
142 --              Returns nil if no joystick. Otherwise returns hold times for all four axis (numeric)
143 --              followed by button states (boolean).
144 --      - jpcrr.keyboard_leds()
145 --              Returns nil if no keyboard, false if LED status is unknown. Otherwise returns three
146 --              booleans, first being state of num lock, second being state of caps lock and third
147 --              being state of scroll lock.
148 --      - jpcrr.mouse_state()
149 --              Returns nil if no mouse. Otherwise returns X, Y and Z axis pending motions (numeric)
150 --              followed by button states (5 booleans).
151 --      - jpcrr.component_encode(table components)
152 --              Return component encoding for specified components.
153 --      - jpcrr.component_decode(string line)
154 --              Return component decoding for specified line, nil/nil if it doesn't encode
155 --              anything, nil/string if parse error occurs (the error is the second return).
156 --      - jpcrr.save_state(string name)
157 --              Savestate into specified file. Returns name used.
158 --      - jpcrr.save_movie(string name)
159 --              Save movie into specified file. Returns name used.
160 --      - jpcrr.load_state_normal(string name)
161 --              Load specified savestate. Returns name used.
162 --      - jpcrr.load_state_preserve_events(string name)
163 --              Load specified savestate, preserving events. Returns name used.
164 --      - jpcrr.load_state_movie(string name)
165 --              Load specified savestate as movie. Returns name used.
166 --      - jpcrr.assemble()
167 --              Open system settings dialog.
168 --      - jpcrr.change_authors()
169 --              Open change authors dialog.
170 --      - jpcrr.exit = function()
171 --              Exit the Lua VM.
172 --      - jpcrr.ram_dump(string name, boolean binary)
173 --              Dump PC memory to specified file. If binary is true, dump is binary, otherwise
174 --              textual hexadecimal dump.
175 --      - jpcrr.write_byte(number addr, number value)
176 --              Write byte to specified physical address.
177 --      - jpcrr.write_word(number addr, number value)
178 --              Write word to specified physical address (little endian).
179 --      - jpcrr.write_dword(number addr, number value)
180 --              Write dword to specified physical address (little endian).
181 --      - jpcrr.read_byte(number addr)
182 --              Return byte from specified physical address.
183 --      - jpcrr.read_word(number addr)
184 --              Return word from specified physical address (little endian).
185 --      - jpcrr.read_dword(number addr)
186 --              Return dword from specified physical address (little endian).
187 --      - jpcrr.read_byte_signed(number addr)
188 --              Return signed byte from specified physical address.
189 --      - jpcrr.read_word_signed(number addr)
190 --              Return signed word from specified physical address (little endian).
191 --      - jpcrr.read_dword_signed(number addr)
192 --              Return signed dword from specified physical address (little endian).
193 --      - jpcrr.timed_trap(number nsecs)
194 --              Set trap after specified number of nanoseconds. Use nil as nsecs to disable.
195 --      - jpcrr.vretrace_start_trap(boolean is_on)
196 --              Set trap on vretrace start on/off.
197 --      - jpcrr.vretrace_end_trap(boolean is_on)
198 --              Set trap on vretrace end on/off.
199 --      - jpcrr.pc_start()
200 --              Start PC execution.
201 --      - jpcrr.pc_stop()
202 --              Stop PC execution.
203 --      - jpcrr.set_pccontrol_pos(number x, number y)
204 --              Set position of PCControl window.
205 --      - jpcrr.set_luaplugin_pos(number x, number y)
206 --              Set position of LuaPlugin window.
207 --      - jpcrr.set_pcmonitor_pos(number x, number y)
208 --              Set position of PCMonitor window.
209 --      - jpcrr.set_pcstartstoptest_pos(number x, number y)
210 --              Set position of PCStartStopTest window.
211 --      - jpcrr.set_virtualkeyboard_pos(number x, number y)
212 --              Set position of VirtualKeyboard window.
213 --      - jpcrr.stringlessthan(String x, String y)
214 --              Return true if x is before y in codepoint lexical order, otherwise false.
215 --      - jpcrr.screenshot(boolean include_hud)
216 --              Take screen shot (Requires monitor). If include_hud is true, include HUD
217 --              (as shown on screen). Note that this should only be called during frame
218 --              hold or results are pretty much undefined.
219 --      - jpcrr.sendevent(string class, string/number...)
220 --              Sends specified event.
221 --      - jpcrr.sendevent_lowbound(number lowbound, string class, string/number...)
222 --              Sends specified event with specified time low bound.
223 --      - jpcrr.movie_rerecords()
224 --              Return number of rerecords (nil if no movie loaded).
225 --      - jpcrr.movie_length()
226 --              Return length of movie in ns (nil if no movie loaded).
227 --      - jpcrr.movie_headers()
228 --              Return headers of movie as array of arrays (nil if no movie loaded).
230 --      I/O functions have the following conventions. If function returns any real data, the first
231 --      return value returns this data or is nil. Otherwise first return value is true or false.
232 --      If first return value is nil or false, then the second return value gives textual error
233 --      message for failed operation, or is nil if EOF occured before anything was read.
235 --      Unlink, rename and mkdir don't follow this pattern. They just return true/false to signal
236 --      success or failure.
238 --      Specifying nil as name of file results random filename being used (it even works with unlink,
239 --      mkdir, rename and read-only access, but doesn't make any sense there).
241 --      Specifying empty filename prompts for file (doesn't work with mkdir, rename nor unlink). Use
242 --      '/<title>' to specify title for prompt dialog.
244 --      Class: BinaryFile:
245 --              Binary file for RO or RW access. Methods are as follows:
246 --              - name()
247 --                      Return name of file.
248 --              - length()
249 --                      Return length of file.
250 --              - set_length(number length)
251 --                      Truncate file to specified length
252 --              - read(number offset, number length)
253 --                      Read up to length bytes from offset.
254 --              - write(number offset, string content)
255 --                      Write content to specified offset.
256 --              - close()
257 --                      Close the file.
258 --      Class: BinaryInput:
259 --              Binary file for sequential input. Methods are as follows:
260 --              - name()
261 --                      Return name of file.
262 --              - four_to_five()
263 --                      Return BinaryInput that is four to five decoding of this stream.
264 --              - text()
265 --                      Return stream as TextInput.
266 --              - inflate()
267 --                      Return BinaryInput that is inflate of this stream.
268 --              - read(number bytes)
269 --                      Read up to bytes bytes from file.
270 --              - read()
271 --                      Read the entiere file at once.
272 --              - close()
273 --                      Close the file.
274 --              Character set for binary files is Latin-1.
276 --      Class: BinaryOutput:
277 --              Binary file for sequential output. Methods are as follows:
278 --              - name()
279 --                      Return name of file.
280 --              - four_to_five()
281 --                      Return BinaryOutput that writes four to five encoded output to this stream.
282 --              - text()
283 --                      Return stream as TextOutput.
284 --              - deflate()
285 --                      Return BinaryOutput that writes deflate output to this stream.
286 --              - write(string content)
287 --                      Write string to file.
288 --              - close()
289 --                      Close the file.
290 --              Character set for binary files is Latin-1.
292 --      Class: TextInput:
293 --              - name()
294 --                      Return name of file.
295 --              - read()
296 --                      Read line from file.
297 --              - read_component()
298 --                      Read next componented line into array.
299 --              - lines()
300 --                      Line iterator function.
301 --              - close()
302 --                      Close the file.
303 --      Character set for text files is UTF-8.
305 --      Class: TextOutput:
306 --              - name()
307 --                      Return name of file.
308 --              - write(string line)
309 --                      Write line line to file.
310 --              - write_component(table components)
311 --                      Write componented line.
312 --              - close()
313 --                      Close the file.
314 --              Character set for text files is UTF-8.
316 --      Class ArchiveIn:
317 --              - member(string name)
318 --                      Open substream for member name. The methods are the same as for io.opentextin.
319 --              - member_binary(string name)
320 --                      Open binary (four to five) substream for member name. The methods are the same as
321 --                      for io.openbinaryin.
322 --              - close()
323 --                      Close the file. Any opened substreams are invalidated.
324 --              - member_list()
325 --                      Return table listing all members of archive (sorted by name).
326 --      Class ArchiveOut:
327 --              - member(string name)
328 --                      Open substream for member name. The methods are the same as for io.opentextout. Note that
329 --                      previous member must be closed before next can open.
330 --              - member_binary(string name)
331 --                      Open binary (four to five) substream for member name. The methods are the same as
332 --                      for io.openbinaryout. Note that previous substream must be closed before next can open.
333 --              - commit()
334 --                      Commit the file. No substream may be open. Closes the file.
335 --              - rollback()
336 --                      Rollback the file. No substream may be open. Closes the file.
338 --      - io.open(string name, string mode) -> BinaryFile
339 --              Open file named @name in specified mode. The mode can be 'r' (read only) or 'rw' (read and
340 --              write).
341 --      - io.open_read(string name) -> BinaryInput
342 --              Open file named @name as binary input stream.
343 --      - io.open_write(string name) -> BinaryOutput
344 --              Open file named @name as binary input stream.
345 --      - io.open_arch_read(string name) -> ArchiveIn
346 --              Open file named @name as input archive.
347 --      - io.open_arch_write(string name) -> ArchiveOut
348 --              Open file named @name as output archive.
349 --      - io.mkdir(string name)
350 --              Create directory name. Returns name created on success, nil on failure.
351 --      - io.unlink(string name)
352 --              Delete file/directory name. Returns name deleted on success, nil on failure.
353 --      - io.rename(string old, string new)
354 --              Rename file old -> new. Returns old, new on success, nil on failure.
355 --      - io.transform.text()
356 --              Returns function that calls text() method of its parameter.
357 --      - io.transform.four_to_five()
358 --              Returns function that calls four_to_five() method of its parameter.
359 --      - io.transform.deflate()
360 --              Returns function that calls deflate() method of its parameter.
361 --      - io.transform.inflate()
362 --              Returns function that calls inflate() method of its parameter.
363 --      - io.dotransform(object obj, function...)
364 --              Call specified functions on specified object. If any function fails (first argument nil
365 --              or false), call close method on preceeding object. Otherwise call specified functions
366 --              chained left to right and return the result.
367 --      - io.dotransform2(object obj, string err, function...)
368 --              Similar to dotransform except if obj is nil or false, returns obj, err.
374 local handle, err, chunk, indication, k, v;
376 local loadmod = loadmodule;
377 loadmodule = nil;
379 local export_module_in = function(tab, modname, prefix)
380         local fun = loadmod(modname);
381         for k, v in pairs(fun) do
382                 tab[(prefix or "") .. k] = v;
383         end
386 jpcrr = {};
387 jpcrr.hud = {};
388 jpcrr.events = {};
389 bit = {};
390 io = {};
392 export_module_in(jpcrr, "org.jpc.luaextensions.Base");
393 export_module_in(jpcrr, "org.jpc.luaextensions.InputDevices");
394 export_module_in(jpcrr, "org.jpc.luaextensions.ComponentCoding", "component_");
395 export_module_in(jpcrr.events, "org.jpc.luaextensions.Events");
396 export_module_in(bit, "org.jpc.luaextensions.Bitops");
398 -- Few misc functions.
399 assert = function(val, err)
400         if (not val) and err then
401                 error(err);
402         end
403         return val;
406 modulus_split = function(number, ...)
407         local dividers = {...};
408         local results = {};
409         local rem;
411         for k, v in ipairs(dividers) do
412                 rem = number % v;
413                 table.insert(results, (number - rem) / v);
414                 number = rem;
415         end
417         table.insert(results, number);
418         return unpack(results);
421 local getmtable = getmetatable;
422 local toString = tostring;
423 local inject_binary_file;
424 local inject_binary_input;
425 local inject_binary_output;
426 local inject_text_input;
427 local inject_text_output;
428 local inject_archive_input;
429 local inject_archive_output;
431 -- Class member injectors.
432 inject_binary_file = function(obj, name)
433         local _name = name;
434         getmtable(obj).name = function(obj)
435                 return _name;
436         end
437         getmtable(obj).__index = function(tab, name)
438                 local x = getmtable(obj)[name];
439                 if x then
440                         return x;
441                 end
442                 error("Invalid method " .. name .. " for BinaryFile");
443         end
444         return obj;
447 inject_binary_input = function(obj, underlying, name)
448         local _name = name;
449         local old_four_to_five = getmtable(obj).four_to_five;
450         local old_inflate = getmtable(obj).inflate;
451         local old_text = getmtable(obj).text;
452         local old_read = getmtable(obj).read;
453         local old_close = getmtable(obj).close;
454         local underlying_object = underlying;
456         getmtable(obj).name = function(obj)
457                 return _name;
458         end
459         getmtable(obj).four_to_five = function(obj)
460                 local res, err;
461                 res, err = old_four_to_five(obj);
462                 if not res then
463                         return res, err;
464                 end
465                 return inject_binary_input(res, obj, "four-to-five<" .. _name .. ">");
466         end
467         getmtable(obj).inflate = function(obj)
468                 local res, err;
469                 res, err = old_inflate(obj);
470                 if not res then
471                         return res, err;
472                 end
473                 return inject_binary_input(res, obj, "inflate<" .. _name .. ">");
474         end
475         getmtable(obj).text = function(obj)
476                 local res, err;
477                 res, err = old_text(obj);
478                 if not res then
479                         return res, err;
480                 end
481                 return inject_text_input(res, obj, "text<" .. _name .. ">");
482         end
483         getmtable(obj).read = function(obj, toread)
484                 if toread then
485                         return old_read(obj, toread);
486                 else
487                         local res = "";
488                         local ret, err;
489                         while true do
490                                 ret, err = old_read(obj, 16384);
491                                 if not ret then
492                                         if not err then
493                                                 return res;
494                                         end
495                                         return nil, err;
496                                 end
497                                 res = res .. ret;
498                         end
499                 end
500         end
501         getmtable(obj).close = function(obj)
502                 local ret, err, ret2, err2;
503                 ret, err = old_close(obj);
504                 if underlying_object then ret2, err2 = underlying_object:close(); end
505                 if ret and not ret2 then
506                         err = err2;
507                         ret = ret2;
508                 end
509                 return ret, err;
510         end
511         getmtable(obj).__index = function(tab, name)
512                 local x = getmtable(obj)[name];
513                 if x then
514                         return x;
515                 end
516                 error("Invalid method " .. name .. " for BinaryInput");
517         end
518         return obj;
521 inject_binary_output = function(obj, underlying, name)
522         local _name = name;
523         local old_four_to_five = getmtable(obj).four_to_five;
524         local old_deflate = getmtable(obj).deflate;
525         local old_text = getmtable(obj).text;
526         local old_close = getmtable(obj).close;
527         local underlying_object = underlying;
529         getmtable(obj).name = function(obj)
530                 return _name;
531         end
532         getmtable(obj).four_to_five = function(obj)
533                 local res, err;
534                 res, err = old_four_to_five(obj);
535                 if not res then
536                         return res, err;
537                 end
538                 return inject_binary_output(res, obj, "four-to-five<" .. _name .. ">");
539         end
540         getmtable(obj).deflate = function(obj)
541                 local res, err;
542                 res, err = old_deflate(obj);
543                 if not res then
544                         return res, err;
545                 end
546                 return inject_binary_output(res, obj, "deflate<" .. _name .. ">");
547         end
548         getmtable(obj).text = function(obj)
549                 local res, err;
550                 res, err = old_text(obj);
551                 if not res then
552                         return res, err;
553                 end
554                 return inject_text_output(res, obj, "text<" .. _name .. ">");
555         end
556         getmtable(obj).close = function(obj)
557                 local ret, err, ret2, err2;
558                 ret, err = old_close(obj);
559                 if underlying_object then ret2, err2 = underlying_object:close(); end
560                 if ret and not ret2 then
561                         err = err2;
562                         ret = ret2;
563                 end
564                 return ret, err;
565         end
566         getmtable(obj).__index = function(tab, name)
567                 local x = getmtable(obj)[name];
568                 if x then
569                         return x;
570                 end
571                 error("Invalid method " .. name .. " for BinaryOutput");
572         end
573         return obj;
576 inject_text_input = function(obj, underlying, name)
577         local _name = name;
578         local old_close = getmtable(obj).close;
579         local underlying_object = underlying;
581         getmtable(obj).lines = function(obj)
582                 return function(state, prevline)
583                         return state:read();
584                 end, obj, nil;
585         end
586         getmtable(obj).name = function(obj)
587                 return _name;
588         end
589         getmtable(obj).close = function(obj)
590                 local ret, err, ret2, err2;
591                 ret, err = old_close(obj);
592                 if underlying_object then ret2, err2 = underlying_object:close(); end
593                 if ret and not ret2 then
594                         err = err2;
595                         ret = ret2;
596                 end
597                 return ret, err;
598         end
599         getmtable(obj).__index = function(tab, name)
600                 local x = getmtable(obj)[name];
601                 if x then
602                         return x;
603                 end
604                 error("Invalid method " .. name .. " for TextInput");
605         end
606         return obj;
609 inject_text_output = function(obj, underlying, name)
610         local _name = name;
611         local old_close = getmtable(obj).close;
612         local underlying_object = underlying;
614         getmtable(obj).name = function(obj)
615                 return _name;
616         end
617         getmtable(obj).close = function(obj)
618                 local ret, err, ret2, err2;
619                 ret, err = old_close(obj);
620                 if underlying_object then ret2, err2 = underlying_object:close(); end
621                 if ret and underlying_object then
622                         err = err2;
623                         ret = ret2;
624                 end
625                 return ret, err;
626         end
627         getmtable(obj).__index = function(tab, name)
628                 local x = getmtable(obj)[name];
629                 if x then
630                         return x;
631                 end
632                 error("Invalid method " .. name .. " for TextOutput");
633         end
634         return obj;
637 inject_archive_input = function(obj, name)
638         local _name = name;
639         local old_member = getmtable(obj).member;
640         local old_member_list = getmtable(obj).member_list;
641         getmtable(obj).member = function(obj, member)
642                 local res, err;
643                 res, err = old_member(obj, member);
644                 if not res then
645                         return res, err;
646                 end
647                 return inject_binary_input(res, nil, _name .. "[" .. member .. "]");
648         end
649         getmtable(obj).name = function(obj)
650                 return _name;
651         end
652         getmtable(obj).member_list = function(obj)
653                 local tab = old_member_list(obj);
654                 if tab then table.sort(tab, jpcrr.stringlessthan); end
655                 return tab;
656         end
657         getmtable(obj).__index = function(tab, name)
658                 local x = getmtable(obj)[name];
659                 if x then
660                         return x;
661                 end
662                 error("Invalid method " .. name .. " for ArchiveInput");
663         end
664         return obj;
667 inject_archive_output = function(obj, name)
668         local _name = name;
669         local old_member = getmtable(obj).member;
670         getmtable(obj).member = function(obj, member)
671                 local res, err;
672                 res, err = old_member(obj, member);
673                 if not res then
674                         return res, err;
675                 end
676                 return inject_binary_output(res, nil, _name .. "[" .. member .. "]");
677         end
678         getmtable(obj).name = function(obj)
679                 return _name;
680         end
681         getmtable(obj).__index = function(tab, name)
682                 local x = getmtable(obj)[name];
683                 if x then
684                         return x;
685                 end
686                 error("Invalid method " .. name .. " for ArchiveOutput");
687         end
688         return obj;
692 -- Redefined routines.
694         local rprint = print_console_msg;
695         print_console_msg = nil;
696         print = function(...)
697                 local x = "";
698                 local y = {...};
699                 local i;
700                 for i = 1,#y do
701                         if i > 1 then
702                                 x = x .. "\t" .. toString(y[i]);
703                         else
704                                 x = toString(y[i]);
705                         end
706                 end
707                 rprint(x);
708         end
709         print_console_msg = nil;
714 -- I/O routines.
715 local stringfind = string.find;
716 local randname = loadmod("org.jpc.luaextensions.DelayedDelete").random_temp_name;
717 local selectname = loadmod("org.jpc.luaextensions.BaseFSOps").opensave_dialog;
718 local path = args["luapath"] or ".";
719 local toresourcename = function(resname, save, text)
720         if not resname then
721                 return randname(path .. "/", "luatemp-");
722         end
724         if resname == "" and text then
725                 local a, b;
726                 a, b = selectname(save, text);
727                 return a, (a or b);
728         end
730         if stringfind(resname, "^/") then
731                 if not text then
732                         error("Bad resource name (case 2): " .. resname);
733                 end
734                 local a, b;
735                 a, b = selectname(save, string.sub(resname, 2));
736                 return a, (a or b);
737         end
739         if not stringfind(resname, "[%d%l%u_%-]") then
740                 error("Bad resource name (case 1): " .. resname);
741         end
742         if stringfind(resname, "%.%.") then
743                 error("Bad resource name (case 3): " .. resname);
744         end
745         if stringfind(resname, "\\") then
746                 error("Bad resource name (case 4): " .. resname);
747         end
749         return resname, path .. "/" .. resname;
754         local openbinin = loadmod("org.jpc.luaextensions.BinaryInFile").open;
755         local openbinout = loadmod("org.jpc.luaextensions.BinaryOutFile").open;
756         local openarchin = loadmod("org.jpc.luaextensions.ArchiveIn").open;
757         local openarchout = loadmod("org.jpc.luaextensions.ArchiveOut").open;
758         local openbinary = loadmod("org.jpc.luaextensions.BinaryFile").open;
760         local baseFS = loadmod("org.jpc.luaextensions.BaseFSOps");
761         local mkdir = baseFS.mkdir;
762         local unlink = baseFS.unlink;
763         local rename = baseFS.rename;
765         local getmtable = getmetatable;
767         loadfile = function(_script)
768                 local file, file2, err, content;
769                 local x, y;
770                 x, y = toresourcename(_script, false, "Select script to load");
771                 if not x then return x, y; end
772                 file, err = openbinin(y, "r");
773                 if not file then
774                         return nil, "Can't open " .. _script .. ": " .. err;
775                 end
776                 file2, err = file:text();
777                 if not file2 then
778                         return nil, "Can't transform " .. _script .. ": " .. err;
779                 end
780                 content = "";
781                 line = file2:read();
782                 while line do
783                         content = content .. line .. "\n";
784                         line = file2:read();
785                 end
786                 file2:close();
787                 file:close();
788                 return loadstring(content, _script);
789         end
791         io.open = function(name, mode)
792                 local _name;
793                 local res, err;
794                 local y;
795                 if mode == "r" then
796                         _name, y = toresourcename(name, false, "Select file to read");
797                 else
798                         _name, y = toresourcename(name, true, "Select file to write");
799                 end
800                 if not _name then return _name, y; end
801                 res, err = openbinary(y, mode);
802                 if not res then
803                         return res, err;
804                 end
805                 return inject_binary_file(res, _name);
806         end
808         io.open_arch_read = function(name)
809                 local _name = name;
810                 local res, err;
811                 local y;
812                 _name, y = toresourcename(name, false, "Select archive to read");
813                 if not _name then return _name, y; end
814                 res, err = openarchin(y);
815                 if not res then
816                         return res, err;
817                 end
818                 return inject_archive_input(res, _name);
819         end
821         io.open_arch_write = function(name)
822                 local _name = name;
823                 local res, err;
824                 local y;
825                 _name, y = toresourcename(name, true, "Select archive to write");
826                 if not _name then return _name, y; end
827                 res, err = openarchout(y);
828                 if not res then
829                         return res, err;
830                 end
831                 return inject_archive_output(res, _name);
832         end
834         io.open_read = function(name)
835                 local _name = name;
836                 local res, err;
837                 local y;
838                 _name, y = toresourcename(name, false, "Select file to read");
839                 if not _name then return _name, y; end
840                 res, err = openbinin(y);
841                 if not res then
842                         return res, err;
843                 end
844                 return inject_binary_input(res, nil, _name);
845         end
847         io.open_write = function(name)
848                 local _name = name;
849                 local res, err;
850                 local y;
851                 _name, y = toresourcename(name, true, "Select file to write");
852                 if not _name then return _name, y; end
853                 res, err = openbinout(y);
854                 if not res then
855                         return res, err;
856                 end
857                 return inject_binary_output(res, nil, _name);
858         end
860         io.mkdir = function(name)
861                 local _name, y;
862                 _name, y = toresourcename(name);
863                 if mkdir(y) then
864                         return _name;
865                 else
866                         return nil;
867                 end
868         end
870         io.unlink = function(name)
871                 local _name, y;
872                 _name, y = toresourcename(name);
873                 if unlink(y) then
874                         return _name;
875                 else
876                         return nil;
877                 end
878         end
880         io.rename = function(name1, name2)
881                 local _name, y;
882                 local _name2, y2;
883                 _name, y = toresourcename(name1);
884                 _name2, y2 = toresourcename(name2);
885                 if rename(y, y2) then
886                         return _name, _name2;
887                 else
888                         return nil;
889                 end
890         end
892         io.transform = {};
894         io.transform.text = function()
895                 return function(obj)
896                         return obj:text();
897                 end
898         end
900         io.transform.four_to_five = function()
901                 return function(obj)
902                         return obj:four_to_five();
903                 end
904         end
906         io.transform.inflate = function()
907                 return function(obj)
908                         return obj:inflate();
909                 end
910         end
912         io.transform.deflate = function()
913                 return function(obj)
914                         return obj:deflate();
915                 end
916         end
918         io.dotransform = function(obj, ...)
919                 local todo = {...};
920                 local k, v;
921                 local obj2, err;
922                 for k, v in ipairs(todo) do
923                         obj2, err = v(obj);
924                         if not obj2 then
925                                 obj:close();
926                                 return nil, err;
927                         end
928                         obj = obj2;
929                 end
930                 return obj;
931         end
933         io.dotransform2 = function(obj, err, ...)
934                 if not obj then
935                         return obj, err;
936                 end
937                 return io.dotransform(obj, err, ...);
938         end
942 -- Various stuff built on top of ECI.
943 local invoke = jpcrr.invoke;
944 local invokecall = jpcrr.call;
945 local invokesync = jpcrr.invoke_synchronous;
947 jpcrr.sendevent = function(...)
948         local arguments = {...};
949         local k, v;
950         for k, v in ipairs(arguments) do
951                 arguments[k] = toString(v);
952         end
953         invokesync("sendevent", arguments);
956 jpcrr.sendevent_lowbound = function(...)
957         local arguments = {...};
958         local k, v;
959         for k, v in ipairs(arguments) do
960                 arguments[k] = toString(v);
961         end
962         invokesync("sendevent-lowbound", arguments);
965 jpcrr.save_state = function(name)
966         local _name, _fname;
967         _name, _fname = toresourcename(name);
968         invokesync("state-save", {_fname});
969         return _name;
972 jpcrr.save_movie = function(name)
973         local _name, _fname;
974         _name, _fname = toresourcename(name);
975         invokesync("movie-save", {_fname});
976         return _name;
979 jpcrr.load_state_normal = function(name)
980         local _name, _fname;
981         _name, _fname = toresourcename(name);
982         invokesync("state-load", {_fname});
983         return _name;
986 jpcrr.load_state_preserve_events = function(name)
987         local _name, _fname;
988         _name, _fname = toresourcename(name);
989         invokesync("state-load-noevents", {_fname});
990         return _name;
993 jpcrr.load_state_movie = function(name)
994         local _name, _fname;
995         _name, _fname = toresourcename(name);
996         invokesync("state-load-movie", {_fname});
997         return _name;
1000 jpcrr.assemble = function()
1001         invokesync("pc-assemble");
1004 jpcrr.change_authors = function()
1005         invokesync("change-authors");
1008 jpcrr.ram_dump = function(name, binary)
1009         local _name, _fname;
1010         _name, _fname = toresourcename(name);
1011         if binary then
1012                 invokesync("ram-dump-binary", {_fname});
1013         else
1014                 invokesync("ram-dump-text", {_fname});
1015         end
1016         return _name;
1019 local wait_event = jpcrr.wait_event;
1020 jpcrr.wait_event = function()
1021         local a, b;
1022         while not a do
1023                 a, b = wait_event();
1024         end
1025         return a, b;
1029 jpcrr.hud.left_gap = function(f, g)
1030         invoke("hud-left-gap", {toString(f), toString(g)});
1033 jpcrr.hud.right_gap = function(f, g)
1034         invoke("hud-right-gap", {toString(f), toString(g)});
1037 jpcrr.hud.top_gap = function(f, g)
1038         invoke("hud-top-gap", {toString(f), toString(g)});
1041 jpcrr.hud.bottom_gap = function(f, g)
1042         invoke("hud-bottom-gap", {toString(f), toString(g)});
1045 jpcrr.hud.white_solid_box = function(f, x, y, w, h)
1046         invoke("hud-white-solid-box", {toString(f), toString(x), toString(y), toString(w), toString(h)});
1049 jpcrr.hud.box = function(f, x, y, w, h, t, lr, lg, lb, la, fr, fg, fb, fa)
1050         invoke("hud-box", {toString(f), toString(x), toString(y), toString(w), toString(h),
1051                 toString(t), toString(lr), toString(lg), toString(lb), toString(la), toString(fr),
1052                 toString(fg), tostring(fb), toString(fa)});
1055 jpcrr.hud.circle = function(f, x, y, r, t, lr, lg, lb, la, fr, fg, fb, fa)
1056         invoke("hud-circle", {toString(f), toString(x), toString(y), toString(r),
1057                 toString(t), toString(lr), toString(lg), toString(lb), toString(la), toString(fr),
1058                 toString(fg), tostring(fb), toString(fa)});
1061 jpcrr.hud.bitmap = function(f, x, y, bmap, lr, lg, lb, la, fr, fg, fb, fa)
1062         invoke("hud-bitmap", {toString(f), toString(x), toString(y), bmap, toString(lr),
1063                 toString(lg), toString(lb), toString(la), toString(fr), toString(fg), tostring(fb),
1064                 toString(fa)});
1067 jpcrr.hud.bitmap_binary = function(f, x, y, bmap, lr, lg, lb, la, fr, fg, fb, fa)
1068         invoke("hud-bitmap-binary", {toString(f), toString(x), toString(y), bmap, toString(lr),
1069                 toString(lg), toString(lb), toString(la), toString(fr), toString(fg), tostring(fb),
1070                 toString(fa)});
1073 jpcrr.shutdown_emulator = function()
1074         invokesync("shutdown-emulator", {});
1077 jpcrr.set_pccontrol_pos = function(x, y)
1078         invokesync("pccontrol-setwinpos", {toString(x), toString(y)});
1081 jpcrr.set_luaplugin_pos = function(x, y)
1082         invokesync("luaplugin-setwinpos", {toString(x), toString(y)});
1085 jpcrr.set_pcmonitor_pos = function(x, y)
1086         invokesync("pcmonitor-setwinpos", {toString(x), toString(y)});
1089 jpcrr.set_pcstartstoptest_pos = function(x, y)
1090         invokesync("pcstartstoptest-setwinpos", {toString(x), toString(y)});
1093 jpcrr.set_virtualkeyboard_pos = function(x, y)
1094         invokesync("virtualkeyboard-setwinpos", {toString(x), toString(y)});
1097 jpcrr.exit = function()
1098         invokesync("luaplugin-terminate");
1101 jpcrr.pc_start = function()
1102         invoke("pc-start");
1105 jpcrr.pc_stop = function()
1106         invokesync("pc-stop");
1109 jpcrr.screenshot = function(include_hud)
1110         if include_hud then
1111                 invoke("screenshot-renderbuffer");
1112         else
1113                 invoke("screenshot-vgabuffer");
1114         end
1118 jpcrr.vretrace_start_trap = function(is_on)
1119         if is_on then
1120                 invokesync("trap-vretrace-start-on");
1121         else
1122                 invokesync("trap-vretrace-start-off");
1123         end
1126 jpcrr.vretrace_end_trap = function(is_on)
1127         if is_on then
1128                 invokesync("trap-vretrace-end-on");
1129         else
1130                 invokesync("trap-vretrace-end-off");
1131         end
1134 jpcrr.timed_trap = function(nsecs)
1135         if nsecs then
1136                 invokesync("trap-timed", {toString(nsecs)});
1137         else
1138                 invokesync("trap-timed-disable");
1139         end
1142 jpcrr.write_byte = function(addr, value)
1143         invokesync("memory-write", {toString(addr), toString(value), "1"});
1146 jpcrr.write_word = function(addr, value)
1147         invokesync("memory-write", {toString(addr), toString(value), "2"});
1150 jpcrr.write_dword = function(addr, value)
1151         invokesync("memory-write", {toString(addr), toString(value), "4"});
1154 jpcrr.read_byte = function(addr)
1155         local t = {toString(addr), "1"};
1156         t = invokecall("memory-read", t);
1157         return (t or {})[1];
1160 jpcrr.read_word = function(addr)
1161         local t = {toString(addr), "2"};
1162         t = invokecall("memory-read", t);
1163         return (t or {})[1];
1166 jpcrr.read_dword = function(addr)
1167         local t = {toString(addr), "4"};
1168         t = invokecall("memory-read", t);
1169         return (t or {})[1];
1172 jpcrr.read_byte_signed = function(addr)
1173         local t = {toString(addr), "1"};
1174         t = invokecall("memory-read", t);
1175         return bit.tosigned((t or {})[1], 7);
1178 jpcrr.read_word_signed = function(addr)
1179         local t = {toString(addr), "2"};
1180         t = invokecall("memory-read", t);
1181         return bit.tosigned((t or {})[1], 15);
1184 jpcrr.read_dword_signed = function(addr)
1185         local t = {toString(addr), "4"};
1186         t = invokecall("memory-read", t);
1187         return bit.tosigned((t or {})[1], 31);
1190 jpcrr.invoke = nil;
1191 jpcrr.invoke_synchronous = nil;
1192 jpcrr.call = null
1194 -- Dofile.
1195 dofile = function(_script)
1196         local chunk, err, indication
1197         chunk, err = loadfile(_script);
1198         if not chunk then
1199                 error("Kernel: Can't load subscript " .. _script .. ": " .. err);
1200         end
1201         return chunk();
1204 local args2 = args;
1205 args = null;
1206 args = {};
1207 for k, v in pairs(args2) do
1208         if (#k > 2 and string.byte(k, 1) == 120 and string.byte(k, 2) == 45) then
1209                 args[string.sub(k, 3)] = v;
1210         end
1212 jpcrr_raw = null;
1215         chunk = null;
1216         loaded, err = pcall(function()
1217                 chunk, err = loadfile(script);
1218                 if not chunk then
1219                         error(err);
1220                 end
1221         end);
1222         if not loaded then
1223                 print("Kernel: Can't load script " .. script .. ": " .. err);
1224                 invoke("luaplugin-terminate");
1225                 while true do end
1226         end
1228         script = null;
1229         indication, err = pcall(chunk);
1230         if not indication then
1231                 print("Kernel: Unprotected error in script: " .. err);
1232                 invoke("luaplugin-terminate");
1233                 while true do end
1234         end