Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
I realize I'm being too serious here, but...
If for some reason the hostage stays on screen long enough, you'll notice that the hostages are teleported away (Harry gives them some sort of locking beacon?)
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
Actually, looks like one would have to mess with debugger to get this to work (and If I'm reading correctly, one could implement cycle-accurate resets using that). Unfortunately, I'm not sure of performance impact of just including the debugging infrastructure (I guess I would need to compile bsnes with debugging enabled to find out; EDIT: Looks like building debugging isn't very great performance hit)
I don'ẗ think it is going to work without that, as System::run() very likely steps a frame, and System::runtosave() doesn't look it will run anything if system is already at stable point.
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
I have looked at libsnes...
Note that libsnes.hpp is only a fraction of functionality of the library. There are also the following problems (that I have identified so far):
From needs pretty much rerecording emulator just to test category:
* Nobody has actually tried, so no idea about how sync-stable it is.
* Savestates can be only made on certain spots, no idea how frequent.
From known sync instability category:
* RTC emulation uses real time (fortunately, games using RTC are rare).
From annoying category:
* Firmware files (for the DSP chips) are accessed internally, so one must load them from the filesystem (one can set path, but not provide the firmware image).
* Sub-frame input may be possible (apart from problems it causes with emulator movie code), but what looks to be the most useful such input (sub-frame reset to corrupt SRAM in specific way) doesn't look to be so.
* Oh, and it is somewhat slow: You need pretty fast computer to run compatibility accuracy core (really the only core that matters with rerecording) at full speed. Forget actually fast "fast forward" on any current computer.
On positive note, libsnes doesn't just emulate SNES, it emulates SGB too.
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
TASVideos.org user names corresponding to of most of Youtube user names in uploaders list have been found out, but the following remain:
* ewrgyjjiu
* SpeedRunnerOfficial
* uethenfaif
* sephroth9999 (reported country does not match with pirate_sephiroth, and also note the spelling difference)
* cranium
* TASVideosArchive
* Cray13aliasMP2E
* A2ZOMG (A2ZOMG?)
Anybody knows who those are?
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
There are number of things wrong with this encode:
* No encoder logo
* No subtitles (that I could find)
* Wacky aspect ratio
* Scaling algorithm makes picture look soft.
* Too low resolution for HD encode (NTSC NES should be 1792x1344)
(Well, that's at least what I could find based on quick look).
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
Looks very useful.
Regarding using MD5:
* This isn't about crypto-security, it is identifying files. And emulators should be able to withstand hostile code anyway.
* And AFAIK, as long as the original isn't specifically constructed for breaking MD5, then one can't find file with identical MD5 (i.e. full second preimage attack on MD5 is infeasible).
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
Next time I encode the WIP, I'll use more conventional methods with codecs&containers for the special encode (mux in silent sountrack instead of using raw x264 output).
And the video codec is h.264 like in near all encodes on this site.
Edit: The 20110520 encode was done prior to this message, so it still uses the same encoding methods (which apparently can cause problems) as the 20110519 encode some had problems with.
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
Doesn't work.
* The size of entire state space is 31*2^992 (roughly 1.3*10^300). So it practically never overflows.
* There are multiple cycles. Any nontrivial cycle (there's trivial cycle of 31 states) is likely enormously long.
* There's no guarantee different seeds are on the same cycle (if there are multiple nontrivial cycles).
* Even if two seeds are on the same cycle, the distance between them is likely huge.
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
There's no dedicated memory watch. Lua scripts (unfortunately, these make the emulator somewhat unstable and are somewhat tricky to make) could output values to Lua console window, to screen (including extending screen area if needed) or to separate window.
Considering how clumsy JPC-RR controls are (yeah, that should be fixed), I either use frame advance or when one has bit more breathing room, advance 0.2s at a time (newer versions have that hotkey by default, it can be configured to r11.2 as well).
Yeah, you should backup the movies. Not so much because of desyncs (pretty improbable unless you are using stuff that just plain does not work right, like joysticks in r11.2) but because of possibility of all sorts of screw-ups (with gameplay and load/save).
Regarding Commander Keen:
I have no idea how luck manipulation works in Commander Keen (sorry), but can offer some hints:
General:
* Hit enter when BIOS says to hit F12 to enter boot menu. For some reason, BIOS likes to wait half a second before doing anything even if you hit enter (it waits a lot more if you don't).
* To get most memory, Hit F8 (it can be hit before the prompt appears), then 'Y' five times and 'N' twice.
* Fastest way to boot is to hit F5 in prompt. Unfortunately memory won't be sufficient for most games.
For Commander Keen 1-3:
* CPU divider 200 might be a good value (speeds up emulation).
* Memory size 512 (speeds up save/load a bit).
For other keen games:
* CPU divider 100 might be a good value (speeds up emulation).
* Memory size 512 (speeds up save/load a bit).
* Enable Hretrace.
* Enable SVGA compat mode in the game.
* The sound working seems to depend on phase of the moon. One can see if it is going to work from sound/music source select (in in-game configuration menu).
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
From IRC discussion:
17:39 <Ilari> How long would this game be if played "properly"?
17:39 <nitrogenesis> about 20 minutes
17:39 <nitrogenesis> it'd be really boring
17:40 <Ilari> Ah, goodie.
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
The usual rate for runs using JPC-RR r11.x (every DOS submission from 2871S onwards) is 125875/1796 fps (but not all use that, for instance Jazz Jackrabbit is 125875/2108 fps during action sequences).
JPC-RR r10.x runs (2870S was the last of those) always use 60fps.
If you use timecode data, be careful as some games may jump between multiple rates and the BIOS bootup is at 125875/1796 fps anyway (and resolution switches can cause timecodes to jump oddly).
Also, timecode output resolution is only 1ms. Timecode data at 1ns resolution could be extracted from the dump.
Oh, and newer versions (sadly, not the r11.2 that has a pre-built binary package) have a menu entry to show what frame rate the game is currently running with.
Oh, here's a script to parse dumps:
Download dumpparse.lua
Language: lua
current_segment_table = nil;
current_ts = 0;
file_offset = 0;
describe_type = function(major, minor)
return (
(major == 0) and (
"VIDEO_" .. (
(minor == 0) and "UNCOMPRESSED" or
(minor == 1) and "COMPRESSED" or
("UNKNOWN" .. minor)
)
) or (major == 1) and (
"PCM_" .. (
(minor == 0) and "VOLUME" or
(minor == 1) and "SAMPLE" or
("UNKNOWN" .. minor)
)
) or (major == 2) and (
"FM_" .. (
(minor == 0) and "VOLUME" or
(minor == 1) and "LOWWRITE" or
(minor == 2) and "HIGHWRITE" or
(minor == 3) and "RESET" or
("UNKNOWN" .. minor)
)
) or (major == 3) and (
"DUMMY_SUBTYPE" .. minor
) or (major == 4) and (
"SUBTITLE_" .. (
(minor == 0) and "SUBTITLE" or
("UNKNOWN" .. minor)
)
) or (major == 5) and (
"GAMEINFO_" .. (
(minor == 65) and "AUTHORS" or
(minor == 71) and "GAMENAME" or
(minor == 76) and "LENGTH" or
(minor == 82) and "RERECORDS" or
("UNKNOWN" .. minor)
)
) or (major == 6) and (
"MIDI_" .. (
(minor == 0) and "DATA" or
("UNKNOWN" .. minor)
)
) or ("UNKNOWN" .. major .. "_UNKNOWN" .. minor)
)
end
conditional_error = function(cond, msg)
if cond then
error(msg);
end
end
string_to_number = function(str)
local i, v;
if not str then
return nil;
end
v = 0;
for i = 1,#str do
v = 256 * v + string.byte(str, i);
end
return v;
end
read_fully = function(file, len)
local ret = file:read(len)
conditional_error(not ret or #ret < len, "Corrupt dump file: Unexpected end of file");
file_offset = file_offset + len;
return ret;
end
read_fully_or_none = function(file, len)
local ret = file:read(len)
if not ret or ret == "" then
return nil;
end
conditional_error(#ret < len, "Corrupt dump file: Unexpected end of file");
file_offset = file_offset + len;
return ret;
end
read_skip = function(file, len)
conditional_error(not file:seek("cur", len), "Corrupt dump file: Can't seek to end of packet");
file_offset = file_offset + len;
end
print_entry = function(chan_num, ts, minor, offset, length)
conditional_error(not current_segment_table, "Corrupt dump file: Entry with no segment table in effect");
conditional_error(chan_num == 0xFFFF, "Internal_error: print_entry() called with chan_num == 0xFFFF");
conditional_error(not current_segment_table[chan_num], "Corrupt dump file: Entry with channel #" ..
chan_num .. " not in segment table");
print(current_segment_table[chan_num].name, ts, describe_type(current_segment_table[chan_num].major, minor),
offset, length);
end
read_segment_table_body = function(file)
current_segment_table = {};
local entries = string_to_number(read_fully(file, 2));
conditional_error(entries == 0 or entries == 65535, "Corrupt dump file: Illegal number of channels " ..
"in segment (" .. entries .. ")");
local i;
for i = 1,entries do
local chan = string_to_number(read_fully(file, 2));
current_segment_table[chan] = {};
current_segment_table[chan].major = string_to_number(read_fully(file, 2));
local namelen = string_to_number(read_fully(file, 2));
current_segment_table[chan].name = read_fully(file, namelen);
end
end
handle_skip = function(file)
current_ts = current_ts + 4294967295;
end
specials = {
[string.char(255, 255, 255, 255)] = handle_skip,
["JPCRRMULTIDUMP"] = read_segment_table_body
};
handle_special = function(file)
local possible_indices = {};
local i = 1;
local mlen = 0;
for k, v in pairs(specials) do
possible_indices[i] = k;
i = i + 1;
end
while true do
local d = read_fully(file, 1);
local i = 1;
while i <= #possible_indices do
if string.sub(possible_indices[i], mlen + 1, mlen + 1) ~= d then
table.remove(possible_indices, i);
else
i = i + 1;
end
end
conditional_error(#possible_indices == 0, "Crroupt dump file: Unknown special type");
mlen = mlen + 1;
if #possible_indices == 1 and #possible_indices[1] == mlen then
break;
end
end
specials[possible_indices[1]](file);
end
handle_packet = function(file, chan)
if chan == 0xFFFF then
return handle_special(file);
end
local tsdelta = string_to_number(read_fully(file, 4));
local minor = string_to_number(read_fully(file, 1));
local len = 0;
while true do
local d = string_to_number(read_fully(file, 1));
if d >= 128 then
len = 128 * len + d - 128;
else
len = 128 * len + d;
break;
end
end
local offset = file_offset;
read_skip(file, len);
current_ts = current_ts + tsdelta;
print_entry(chan, current_ts, minor, offset, len);
end
process_stream = function(file)
file_offset = 0;
while true do
local chan = string_to_number(read_fully_or_none(file, 2));
if not chan then
return;
end
handle_packet(file, chan);
end
end
if not arg[1] then
error("Filename needed");
end
local file, err = io.open(arg[1], "rb");
if not file then
error("Can't open " .. arg[1] .. ": " .. err);
end
process_stream(file);
file:close();
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
Moreover, from mplayer on archive 512:
Starting playback...
Movie-Aspect is 0.96:1 - prescaling to correct movie aspect.
VO: [x11] 288x384 => 370x384 Planar YV12
Where is that 0.96:1 aspect ratio (DAR) from? 512s should always be prescaled (in this case to 288x384, which was correct) with PAR of 1:1, which would give DAR of 0.75:1 in this case.
That is, the 512 should be encoded (in this case) at 288x384 with PAR of 1:1.
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
Easier Way: Snapshot -> Ram Dump -> Binary (or Hexadecimal). Then you are prompted for the filename to save as.
At least some Java implementation have a bug here: Dragging from Snapshot to Binary/Hexadecimal might not work. Click on snapshot and ram dump to open the (sub)menus (or point at correct menu entry and hit <ENTER>).
Oh, BTW, the "true" there selects binary mode. Also, the JPC-RR physical memory is not in one chunk in OS memory (chunk is 4096 bytes), so you might need to search for sequence ending in correct value and sequence starting with the correct value.
That is: If the memory address is near the beginning of the chunk, searching for string ending in the correct value won't find it. You need to search for string starting with the correct value (to avoid hitting chunk boundaries).
If in the other hand, the address is near the end of the chunk, the situation is vice versa: You need to search for string ending in the correct value.
Of course, since chunks are 4096 bytes, most addresses are not near either the start or the end of the chunk. For these cases, either string starting or ending at correct value works. The case in post above is such case: The y-position was at 0x2B0D8, which is over 200 bytes from start of the chunk (and much further from the end).
Oh, and here is slightly better memory watch script (draws the values on screen):
Download dgeneration.lua
Language: lua
on_redraw = function()
-- Make some empty space to right, so we don't have to write over game graphics.
-- The first parameter (1) controls when the empty space will appear. 1 => Screen only, 2 => Dump only
-- 3 => Both.
-- The second parameter (160) is size of the empty space. In this case, 160 pixels wide (20 characters).
jpcrr.hud.right_gap(1, 160);
-- The x-position to place texts at. Usually this will be 640 ingame, but in case it isn't...
pos = jpcrr.vga_resolution();
-- Write some text.
-- 1st parameter (1): When the empty space will appear (same meanings as first argument of right_gap).
-- 2nd parameter (pos): x-position to write the text to.
-- 3nd parameter (0): y-position to write the text to.
-- 4th parameter ("ypos=" .. jpcrr.read_byte(0x0x2B0D8)): The text to write.
-- 5th parameter (false): If true, linefeed in text causes line change in printed text.
-- 6th parameter (255): Foreground red (0-255).
-- 7th parameter (255): Foreground green (0-255).
-- 8th parameter (255): Foreground blue (0-255).
-- 9th parameter (255): Foreground alpha (0-255).
-- 10th parameter (0): Background red (0-255).
-- 11th parameter (0): Background green (0-255).
-- 12th parameter (0): Background blue (0-255).
-- 13th parameter (0): Background alpha (0-255).
jpcrr.hud.chargen(1, pos, 0, "ypos=" .. jpcrr.read_byte(0x2B0D8), false, 255, 255, 255, 255, 0, 0, 0, 0);
-- Write another text (in this case, number of current frame). The text height is 16, so write it at
-- y-position 0 + 16 = 16.
jpcrr.hud.chargen(1, pos, 16, "frame=" .. jpcrr.frame_number(), false, 255, 255, 255, 255, 0, 0, 0, 0);
end
-- Register on_redraw to run on each frame received.
jpcrr.register_redraw_function(on_redraw);
-- Wait for events.
while true do jpcrr.wait_event() end
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
This seems to work for converting from 1 player to 2 players (joypads only):
- Read bytes 0x1C-0x1F of the file. This is 32-bit controller data offset (little-endian)
- Copy that amount of bytes, changing byte 0x14 from 0x01 to 0x03
- Until end of file:
- Read 2 bytes
- If these bytes are 0xFF 0xFF, write 0xFF 0xFF 0xFF 0xFF
- Otherwise, write those 2 bytes followed by 0x00 0x00
- Repeat
That method is very hacky but seems to work (modulo game potentially desyncing due to number of controllers changing).
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
The .dtm.
Firstly, you likely should use hard difficulty, as per Wiki: Guidelines: "Where a game has multiple difficulty levels, it is preferred to play on the hardest difficulty level (for more interesting gameplay) unless the only difference between difficulty levels is enemy/boss hit points, in which case the easiest difficulty levels are preferred in the interest of speed."
Secondly, using the DLC versions could cause problems with others getting the .dtm to reliably play back (or to play back at all). Since basically DLC modifies the game and it is no longer what was originally on the disc. You could of course do a quick test run, post the .dtm and ask others if they can play it back.
So I think you should start with Hard (no DLC) run.
Emulator Coder, Experienced Forum User, Published Author, Skilled player
(1142)
Joined: 5/1/2010
Posts: 1217
Basically the encoding method is the same (split audio/video, run both through encoders and mux). The differences come from logo insertion and subtitling. I think the older mencoder method (still documented in Wiki: EncodingGuide) should work.
Unfortunately, there doesn't seem to be a good way on OS X to dedup the encode if game runs at substantially less than full frames per second (omitting dedupping improves player compatibility at cost of increased file size). Sure, some programming would do the trick (but that's programming).