Posts for r57shell


1 2
6 7 8
15 16
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Win3.11 over FreeDos doesn't work. Just Win95 works. Link to video Had to do few hacks. It is require 500+ MB disk, and you can't make it using FDISK. So, I had to use 2GB premade disk from tutorial yet again. To be able to install on it, you should run "SETUP /?" to see parameters. And there will be /is /id <- disable available disk space check, and disable disk scan. Otherwise it will blame that your disk configuration is suspicious, And it will also scan whole disk sector by sector, and eventually fail in the middle. After installing, I had issue - hard disk wasn't bootable. I was able to boot windows using grub for dos. Awesome utility. About it later. I boot it using this grub4dos and made Win95 bootable disk, and using it, I just run "sys a: c:" and after it, my HDD became bootable. SoundBlaster16 installer doesn't work. But there is workable driver in windows itself. All you need is to select it. CD Audio works out of box (all you need is to install MCI CD Audio) Looks like this is because it's using Oak CD-ROM driver. In Win 3.11 I had issue with LG CD-ROM driver. When I first time saw wrong colors of the game, I thought it's driver issue. So I've tried to change it to ET4000 one, but it crash display manager. It doesn't work. But according to some of ET4000 docs, Win95 detects it fine. So, looks like it's not display issue. I guess, you just can't have two 256 colors palette on screen at once. Grub4dos Very detailed Manual: http://diddy.boot-land.net/grub4dos/Grub4dos.htm Downloads: https://sourceforge.net/projects/grub4dos/files/GRUB4DOS/ Note: Download from directory. Exe is for windows. So. 1) copy any of floppy image. 2) delete all files from it. 3) upload unpacked grub4dos without "chinese" dir it will be enough space. leave it as "grub4dos" floppy. Next, boot from dos floppy image. Because this image is not bootable itself. Hotswitch floppy image to this grub4dos floppy. Run it. You'll see ~8 entries of menu. Each one you can press "e" to edit rules for boot. Or press "b" for boot. I took this one from manual:
title Boot CD-ROM - (cd) 
chainloader (cd) 
rootnoverify (cd) 
I did it when I was trying to install ReactOs. Because all it has is bootable CD. It didn't work. But I edit this rule on fly into additional suggestion from manual:
title Boot CD-ROM - (cd) 
cdrom --init
map --hook
chainloader (cd0) 
rootnoverify (cd0) 
And it worked. It started to boot ReactOs disk. Then it crashed somewhere. So, To boot Win95 I did same but there is already premade entry for Win9x and Win ME. Next goal is to check Diablo, and Diablo II :) :>
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
First of all... I want to mention few things that noone mentioned: 1) You have to press Scroll Lock to enable UI Menu keys: TAB for example. 2) You have to use 512,63,16,X disks in notion of dosbox: https://www.dosbox.com/wiki/IMGMOUNT So, 512 bytes per sector, 63 sectors per track, 16 heads, X cylinders. This is for "luck" Because I had no luck for installing on any hdd which was made by chdman by default. 3) No one said about et4000.zip. btw, newest mame require kb_ms_natural.zip 4) I have no idea why I should press F1/F2 or idk I just hit some of them, while seeing message "Starting MS-DOS" at the beginning of the installation. Otherwise it hangs. 5) No one said that you should disable mouse in UI Menu to install Win 3.11 (thanks to one of other tutorials of InsertMoreCoinsblog IMC) Everything below is about Windows 3.11 I have good news. But lets start from beginning. Few days ago, we had this: If you try to run any game that is using Win32S.dll you had this screen for few miliseconds, and then back to DOS prompt. But then, I started to dig into it hardly, and while I was doing so, I was blaming that this address translation makes me nuts. And, suddenly this appeared: https://tea-ci.org/mamedev/mame/16524 Lately, I'll confirm that it's indeed fixing Win32s.dll After I did confirm, that new build is sucessfully running game with Win32s.dll, I find that yet again, I have no music. Then, after few days of digging into Win 3.11 kernel stuff, we've finally tracked the issue. Log with all explanations and discussions here: https://gist.github.com/realmonster/329339dc4cafed2d92a262569cc04e1b Answers on my unanswered questions that appears in log: Q: - does it work with audio discs via sb16? A: - yes it does Q: - watchpoints is set on physical or logical address? A: - watchpoints is set on physical address Q: - is there easy way to extract all files from .chd image? A: - I did it in following way: 1) chdman extractraw. I don't remember full command line, but there is only input and output, nothing else. 2) open DOSBox, 3) "mount D g:\win31" <- this mounts win31 directory as disk. 4) "imgmount C g:\win32.img -t hdd -fs fat -size 512,63,16,4161" <- here win32.img is result of chdman extractraw. 5) "mount E D:\DOS\NC" <- here D:\DOS\NC is your directory with Norton Commander 6) use ALT+F1 to select disk at left part, and ALT+F2 on right part. 7) use Insert to select all files you need. 8) Press F5 to copy, select checkbox about subdirectories. 9) Press Enter. Done. Q: why "source" command in mame debugger doesn't work A: it works. it's executed when next breakpoint being hit. odd! Also, useful thing: in console you may write "print (EAX&0xFFFF)+DSBASE" to see logical address of ds:[ax] or directly in Memory view enter (EAX&0xFFFF)+DSBASE to see that location. How I was investigating: To find where is loading of executables happens, I set watchpoint on address translation table Then, I found where entry point is called. This helped a lot. Using this, I found that WIN32S.exe is loading bunch of others,and in the end WIN32S16.DLL. After its entry point executed, nothing else was load and crash appeared. I was trying to track it down, but no suceess. Because it's somewhere in kernel, caused by bad pointers. Then, when crazyc made fix, and nightly build appeared, I continued my research. First, I was able to "get in" game executable entry. It's tricky task, because code is running ultra fast, and all initialization is done at the begnning. What helped me here? WIN32S16.DLL was crashed before fix, and its entry point was investigated by me thoroughly. Fortunately it has GetProfileString close to the top. Game executable initialization calls this function too! This was my trump card. I set there breakpoint, and step out until I'll see code of game executable. And it worked! It was only beginning of the journey. Next, I found that AIL_redbook_open() fails. But it's located in MSS32.DLL. Then, it is calling 16 bit version from MSS16.DLL. Which is using MciSendCommand from MSSYSTEM.DLL which fails for some reason. Then I had to find out why MciSendCommand fails. It turns out, that OpenDriver(MCICDA.DRV) from USER.EXE fails. Then, it turns out that DRIVERFUNC from MCICDA.DRV fails with certain input. And "finally" (as I thought), that driver function is failed because interrupt by MSCDEX returns data without specific flag - cdrom is able to play audio. So I made patch of driver itself, to ignore flag, and music was working. But nope, it wasn't the end of story. Then we dig into QSCDROM.SYS, and found: looks like mame response on "mode sense command" page 2a with missing header. I guess, this ends my reversing strike in last few days. Result: Link to video I was recording in slowmotion, because avidump is slow. So, that's why reaction is too fast. Also, game speed is weird. In this game you can change sync method by F8. But I'm lazy to record it again. Also, I had wrong old CUE file. I have fixed one and bugged one. I have mixed them, so I use here bugged instead of fixed. Also, windows and dos screens are cropped. Btw: New fix arrived. https://tea-ci.org/mamedev/mame/16569
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
First of all, I'll describe how banking is working here, in ddragonw. Bank is located from 4000 to 8000 in address space. So this chunk is replaced from another if new value is writen in bank register. Bank register is byte at address 3808. So, when something is written in 3808, it sets bank. Bank index is stored in highest three bits of that byte. Other bits is used for other stuff. If we look at code parts where write in 3808 appear, everywhere we see write at address 003A. It's backup register value, implemented in software. It is for transparent code. When something is need to change bank, it will change it, do stuff, and then it will change it back. So, each time we want to know current bank, we can do it by typing "print b@3a & E0" Now, bank values and corresponding chunks of data: 00: 21j-2-3.25 first half (0-4000) 20: 21j-2-3.25 second half (4000-8000) 40: 21a-3.24 first half (0-4000) 60: 21a-3.24 second half (4000-8000) 80: 21j-4.23 first half (0-4000) A0: 21j-4.23 second half (4000-8000) Also, in addition, 8000-FFFF of maincpu address space is whole 21j-1.26 Now, ground fetching code is located at $4000 offset when bank is 80. This means it's at offset 0 in 21a-3.24. It is library function. I mean someone in ddragon team made it as utility. Its usage is following. Programmer needs to set X register to enemy/player, and fill structure located at address B31. Output of library is stored in same structure. I'm lazy to calculate its size. But here is its structure: B31: xpos current (word) B33: ypos current (word) B35: zpos current (word) B37: xpos next (word) B39: ypos next (word) B3B: zpos next (word) B3D: byte_3 (byte) B3E: four samples. each sample is x,y,z byte coords. 12 bytes total. B4A: unknown output (byte) B4B: unknown output (byte) (looks like always zero) B4C: unknown output (byte) (looks like always zero) B4D: unknown output (byte) (looks like always zero) B4E: word xpos result - output B50: word ypos result - output B52: word zpos result - output After this address, temporary data used by this library is stored: B54: X register backup (word) (enemy/player offset) B56: map cell offset (word) B58: iteration (byte) (sample id counting from 1) B59: direction of change (byte) (range is [0,8], 9 directions. 0 - didn't move) B5A: x pos for map (word) B5C: unknown B5D: unknown B5E: y pos for map (word) B60: z pos for map (word) B62-B68: unknown B69-B6D: 5 byte heights. B6E and above - unknown. NOTE: even though all addresses are BXX, in code they appear without B. Because B is set into DP register, which is concatenated in front to address when offset is byte. This indexing mode is called "direct". I had confusion with it until I read about this. Structure of calculations: 1) 4000-4038 pc: initialization + transform coords into map space. transformation looks like: x_map = x, y_map = y, z_map = y+z. 2) 4038: JSR $407F calculation of B59 byte. 3) 403B-4051: first result calculation: x_result = x_map+xshift, y_result = y_map + y_shift, z_result = z_map + y_shift + z_shift, 4) 4053: JSR $40AF sampling 5) 4056: JSR $4507 unknown additional processing. 6) 4059-4077: reverse transformation of resulting coords. Structure of sampling: 1) 40AF-40B1: iteration initialization 2) 40B3: JSR $40E6 - get sample pos: it's just result + sample[iteration-1], where result is x,y,z result, and sample is one of four samples at B3E. If all x,y,z values of sample coords is zero, it's skipped by branching at 40B7. 3) 40B9: JSR $4154 - get map cell. its result in B56 4) 40BC-40DB: loop over cell data. 5) 40DD-405E: increment iteration and continue loop. Map structure: It is located at 4780 in same bank. Offsets starting IDK where, but looks like 4800 already offsets. Each offset is offset at 32 offsets. Then, each of this 32 offsets is offset at list of offsets until word=FFFF. Offsets in the list is actual data. Their 7-th byte is height mask though. (which height byte should be overwriten to FF) Step (4) has few JSR-s which does actual processing of cell data. I didn't dig into details. But when character lands, its z_result overwriten at 4299. Normaly, enemy is filling this structure and then calls this library, and then, enemy is reading result back as is, without any postprocessing.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
This time it also shows fractional part of positions, and shows speed. And, it shows same info for players.
Language: Lua

function draw_enemy(base, id, yy) if memory.readbyteunsigned(base) < 0x80 then return end local x = memory.readwordunsigned(base+4) local y = memory.readwordunsigned(base+6) local z = memory.readwordunsigned(base+8) local xfrac = memory.readbyteunsigned(base+10) local yfrac = memory.readbyteunsigned(base+11) local zfrac = memory.readbyteunsigned(base+12) local xspeed = memory.readwordunsigned(base+0xF) local zspeed = memory.readwordunsigned(base+0x11) local hp = memory.readbyteunsigned(base+0x1F) gui.text(0, yy, string.format("%d: %4X%02X %4X%02X %4X%02X %4X %4X %d", id, x, xfrac, y, yfrac, z, zfrac, xspeed, zspeed, hp)) end function draw_enemies() for i = 0, 8 do local base = 0x45E+0x55*i draw_enemy(base, i, 32+i*8) end end gui.register(function() draw_enemy(0x3A2, 0, 0) draw_enemy(0x400, 1, 8) draw_enemies() end)
You can also look at byte 2 and byte 3 in enemy structure, because landing behavior depends on it.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
sugarfoot wrote:
My mistake was concluding the enemy location objects and HP objects were tracked separately (digging through the source code and sprite data got me off the rails).
It is tracked separately, as far as I know. If you didn't notice, enemy struct size is 0x55, and player struct size is 0x5E. Maybe they share some of funcs usage, will see.
sugarfoot wrote:
- I'll take a step back from pushing through another TAS and focus on fundamentals - doesn't look like I'll get to my sub 6 second runs until I master this level of TAS'ing
I don't think you should dig into this. Edit: I have found all landing-code that we need. I don't know, should I post it now, or you want to find it yourself. Spoiler: it's way too complex.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
sugarfoot wrote:
Question - how were you able to find this table so quickly?
To do it fast, you need two features in your emulator: 1) Ram Search 2) Breakpoints on write and pc. Mame has both. If your emulator doesn't have (1) you can use CheatEngine. But then you need to convert host addresses (x86 for example) into game addresses. Breakpoints can't be replaced. So, this is basic tools that you need to make research quickly. To use that tools in mame, you need to know that there is "-debug" option. So, to run debugger you need to run mame like this:
mame-rr.exe dragonw -debug
This will start your game with debugger opened. Then, you may type "help" in bottom input field. it will show help. First, you need to have idea how are you willing to find your info. In this game, idea is simple. Find hp, because you need it anyway. First, you need to start level in the game and pause the game using P button. Then you do Ram Search: 1) you type "help", to see topics. 2) you type "help cheats", because ram search is in this category. 3) you type "help cheatinit" to show command usage. 4) you type "cheatinit ub,0,0x1000 5) unpause the game and play a bit without taking damage 6) pause the game 7) type "help cheatnext" to see help how to sieve 8) type "cheatnext eq" to leave addresses which still has same value. 9) optionally repeat from step 5 few more times 10) unpause the game and take damage 11) wait while it will affected for sure, and pause again 12) type "cheatnext ne" to leave addresses which value have changed 13) optionally repeat few times from step 10 Now, you should have narrow range of addresses. For example, I did it right now, and after step 9 I have "3976 cheats found" After first execution step 12 I had "37 cheats found". Second "15 cheats found". Third: "10 cheats found". Fourth: "1 cheats found". Now, use "cheatlist": Address=03C1 Start=35 Current=02 Note: 0, 0x1000 in cheatinit it's based on info from mame's ddragon.cpp It's maincpu RAM. If cheatlist is still big, you can check all addresses one by one by looking at them using Memory View for example, or script. Now, when you have some address of player, you may set breakpoint on write. 1) type "help watchpoints" 2) type "help wpset" 3) type "wpset 3C1,1,w" 4) take damage This above is basic strategy. But I did a bit different. I did step 3 at title screen. Then clicked start input button.
Stopped at watchpoint 1 writing byte to 000003C1 (PC=8230) (data=40)
This is where actual reverse begins.
8224: 8E 82 57         LDX   #$8257
8227: A6 86            LDA   A,X
8229: 97 55            STA   $55
822B: 86 40            LDA   #$40
822D: B7 03 C1         STA   $03C1
8230: B7 04 1F         STA   $041F
8233: 7F 03 EF         CLR   $03EF
8236: 7F 04 4D         CLR   $044D
8239: 0D 26            TST   $26
823B: 27 18            BEQ   $8255
823D: CC 00 00         LDD   #$0000
This code is taken using "help memory" and then "dasm qweqwe,0x8224,0x30" then it was in file qweqwe. So, you see here $03C1, $041F :) You can make suggestions that it's both players hp. Nothing you can tell from this. But lets hit Debug Run (F5).
Stopped at watchpoint 1 writing byte to 000003C1 (PC=41C3) (data=0)
41BF: 34 31            PSHS  Y,X,CC
41C1: 6F 80            CLR   ,X+
41C3: 31 3F            LEAY  -$1,Y
41C5: 26 FA            BNE   $41C1
You see it's caused by "CLR" which is obviously "clear" (set zero). Skip further by pressing Debug Run (F5) Again
Stopped at watchpoint 1 writing byte to 000003C1 (PC=41C3) (data=0)
But it shows in title "sub" which means "sub cpu" we don't need that. Run again.
Stopped at watchpoint 1 writing byte to 000003C1 (PC=61EC) (data=40)
Now, this is what we was looking for:
61E4: 2A A7            BPL   $618D
61E6: 01 86 40         OIM   #$86,$40
61E9: A7 88 1F         STA   $1F,X
61EC: 6F 88 1B         CLR   $1B,X
61EF: 6F 88 37         CLR   $37,X
61F2: 6F 0E            CLR   $E,X
61F4: 6F 03            CLR   $3,X
61F6: 6F 88 13         CLR   $13,X
61F9: 6F 88 14         CLR   $14,X
61FC: 6F 88 15         CLR   $15,X
61FF: 6F 88 16         CLR   $16,X
6202: 86 07            LDA   #$07
6204: A7 02            STA   $2,X
6206: 10 8E 62 3A      LDY   #$623A
620A: 96 36            LDA   $36
620C: 84 02            ANDA  #$02
620E: A7 E4            STA   ,S
6210: 48               ASLA  
6211: AB E4            ADDA  ,S
6213: 31 A6            LEAY  A,Y
6215: EC A1            LDD   ,Y++
6217: ED E4            STD   ,S
6219: 96 36            LDA   $36
621B: 26 0B            BNE   $6228
621D: 0D 2A            TST   $2A
621F: 27 07            BEQ   $6228
6221: EC E4            LDD   ,S
6223: C3 00 30         ADDD  #$0030
6226: ED E4            STD   ,S
6228: EC E4            LDD   ,S
622A: ED 04            STD   $4,X
622C: EC A1            LDD   ,Y++
622E: ED 06            STD   $6,X
6230: EC A1            LDD   ,Y++
6232: ED 08            STD   $8,X
6234: 7F 0E 30         CLR   $0E30
6237: 32 64            LEAS  $4,S
6239: 39               RTS  
All those <v>,X where <v> is: $1B, $37, $E, $3, $13... And all of them addressed from X. And X = $3A2. This totally looks like "initialization of player structure" :). And all those offsets are fields in player structure. Thus, size of player structure is greater than $37. Remember it. If we wait until that RTS by pressing F10 (Step Over), we will see that it goes back to B372:
B369: B3 6C 39         SUBD  $6C39
B36C: BD FD A0         JSR   $FDA0
B36F: BD 40 7B         JSR   $407B
B372: BD FD B2         JSR   $FDB2
B375: 39               RTS   
and then (again wait until RTS by pressing F10) to B35F:
B357: 8E 03 A2         LDX   #$03A2
B35A: 0F 2A            CLR   $2A
B35C: BD B3 6C         JSR   $B36C
B35F: 96 29            LDA   $29
B361: 2A 08            BPL   $B36B
B363: 30 88 5E         LEAX  $5E,X
B366: 0C 2A            INC   $2A
B368: BD B3 6C         JSR   $B36C
B36B: 39               RTS   
Main hint here is: it calls $B36C twice, and second call is after incrementing X by $5E. (LEAX $5E, X) Remember that X is player structure? :) This gives us suggestion that next player is located at X+$5E = $3A2+$5E = $400. Remember that player struct size is greater than $37? So, this gives us suggestion that $5E is size of player struct. You can't say at this point that it is in fact true. But I can now say, after I was digging code much harder than discussed above and below. What more interesting is code at B36F. It is JSR that took us to player initialization code. So, we can put there breakpoint using "bpset B36F", then type "softreset", and see what happen. You can also find code that is decreasing player's hp but I don' want it, so I type "wpclear 1", to not disturb me. Okay, after softreset and pressing start at title screen we have:
Stopped at breakpoint 1
Press F11 (Step Into).
407B: 7E 61 D5         JMP   $61D5
Press F11 (Step Into). So, everything in disasm up to 61E9 was wrong, and correct this part is:
61D5: 32 7C            LEAS  -$4,S
61D7: 0D 36            TST   $36
61D9: 27 04            BEQ   $61DF
61DB: 6D 84            TST   ,X
61DD: 2A 58            BPL   $6237
61DF: 86 C1            LDA   #$C1
61E1: A7 84            STA   ,X
61E3: 96 2A            LDA   $2A
61E5: A7 01            STA   $1,X
61E7: 86 40            LDA   #$40
61E9: A7 88 1F         STA   $1F,X
This is due the fact that disasm listing does not know where it came from. Okay, what is also interesting in that code part that is below, is this:
6228: EC E4            LDD   ,S
622A: ED 04            STD   $4,X
622C: EC A1            LDD   ,Y++
622E: ED 06            STD   $6,X
6230: EC A1            LDD   ,Y++
6232: ED 08            STD   $8,X
You can either spot it now, or find X or Y or Z pos location using RAM Search. And then, eventually stumbling across it. So you can say where it is loading X, Y, Z initial player position. Summary: you could find player struct in different ways, using hp/pos other stuff. Enemy hp/pos is found in similar way. But code that iterates among them is more complex. And from that code you can tell that Enemy is skipped if it has negative byte at offset 0. memory.readbyte in mame-rr Lua for some reason returns unsigned one, so check is transformed into "grater than 0x80". Why some enemy doesn't fall, can be tracked down using write-breakpoint on its Z coordinate. And addresses where it does write in Z I posted. Idea for grabbind that addresses is to use this great feature:
wpset 465,1,w,1, {printf "%X", pc; g}
this will print PC value when breakpoint is being hit, and continue without suspending. You can consider me as pro reverser. That is why it's quick for me. Anyway it took 4 hours to find it + figure out how ddragon mapper works.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Ok, here is my fresh new script
Language: lua

function draw_enemy(base, id) if memory.readbyteunsigned(base) < 0x80 then return end local x = memory.readwordunsigned(base+4) local y = memory.readwordunsigned(base+6) local z = memory.readwordunsigned(base+8) local hp = memory.readbyteunsigned(base+0x1F) gui.text(0,id*8,string.format("%d: %4X %4X %4X %d", id, x, y, z, hp)) end function draw_enemies() for i = 0, 8 do local base = 0x45E+0x55*i draw_enemy(base,i) end end gui.register(function() draw_enemies() end)
It shows positions and hp. Make sure you have simlar all three coordinates... not just one or two. I would call them X, Y, Z. X - left/right, Y - far/near, Z - top/bottom. I have found corresponding code, but I don't wanna dig into it right now. Tell me does it something new, or you had this already? If you curious: at least two locations where Z is used: 5318, 40A0. It means, that they are in bank, at offsets from 0x318 and 0x10A0. Bank is most probably in 21j-2-3.25 at offset 0, so it's 0x318 and 0x10A0 there. But you may just put type "bpset 5318" in debugger, and "bpset 40A0".
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Drawings might be few pixels shifted, but it should show things in general.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Enemies spawn is handled by tracing map by scroll position at steps 16x16. This means, that if scroll position is changing 16x16 cell by crossing its border, then appropriate callback is called. If scroll position cross vertical border of the cell from left to the right, then it will call right border callback. All other callbacks works similarly. Each callback does lookup in tree structure of enemies. Lets call it "vertical callback" if it's triggered when scroll position have crossed horizontal border of cell, and call it "horizontal callback" if it's triggered when scroll position have crossed vertical border. So, following picture:
|-V-|
|   |
H   H
|   |
|-V-|
V - vertical callbacks, H - horizontal callbacks. There are two trees of enemies. One is for vertical callbacks, another is for horizontal callbacks. For vertical callbacks, enemies sorted vertically, for horizontal callbacks enemies sorted horizontally. When horizontal callback called, it'll look for exact "new" ahead x position, and then it'll look among all y positions. Similarly, when vertical callback called, it'll look for exact "new" ahead y position, and then it'll look among all x positions. Key point here is that it looks "exact" something. It is all exciting of course, but we need to get practical results. To achieve that, after a lot of headache I did transform that ugly ahead positions into offsets from center of screen. So, instead of drawing spawning points of enemies, I'm drawing regions on screen where center of screen should lay to trigger corresponding callback. Now, a bit explanation of "what we see". Horizontal very wide green box - vertical callback if screen moves UP. Horizontal very wide red box - vertical callback if screen moves DOWN. Vertical very tall green box - horizontal callback if screen moves RIGHT. Vertical very tall red box - horizontal callback if screen moves LEFT. NOTE: it may not trigger if center of screen is inside box but 16x16 cell haven't changed. My script also draws: 1) Aero hitbox 2) Enemies hitboxes 3) Stars hitboxes Use iteration method as I do, because it's how game does. Aero hitbox a bit tricky, it depends on move. (called state in the script) Adapt it for bizhawk yourself. I'm tired. NOTE: it's for Gens-rerecording v11 NOTE 2: it will throw error "stack overflow" if it's not in level. https://pastebin.com/mgLLaCnV A bit of extra notes. Most of positions is using 4 byte fixed point arithmetic. This includes: position of aero, enemies, speed of aero. To utilize subpixels, check my donald tas script for example. Aero speed is located at: FF018E, FF0192. Sometimes is better to use calculated speed. I mean difference between positions in two consecutive frames.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
First, while searching info about fairies, I checked the game for level select similar to Japan version. It's same, just requires character name to be TAKO. I had found this before info about fairies. So, you need to enter name TAKO and then, you may select some stages of round 4 by holding A, or B, or C in round selection. Next, I did research about fairies. Info about that is above. Also I got plenty of RAM watch addresses... Next, I decided to make some replay just to avoid playing the game manualy. While doing that I've found that you may make character name selection screen to fade in faster if you hold start. Suddenly, I decided to dig into coins x2 chance. And I've found that there is double-coins chance located at FFAF56. Surprisingly it depends on name as well. So, I picked one of highest luck character names, having minimal input. Here is my test run. Link to video http://tasvideos.org/userfiles/info/47311537298098294 For some reason I forgot to hold start at character selection screen. So, you may compare frames. As you may see, on the stage with clouds I got all coins around tower doubled, except one that is missed to avoid "perfect bonus". Btw I did damage boost on the top. My run has around 2 k coins more, and around 100 frames ahead of your WIP at the end of this stage. But I have lower hp, yeah. Next I picked up sword upgrade, and lost frames in second stage. I was just testing this strategy. So now I think I should get max magic orbs as you did. In the end, I was so upset about golem boss with hands. He is annoying. Had bad luck to beat him, so I just set sword on autofire and picked up best from some of attempts. Whole run is not optimized too much, but it is a bit optimized. 3.5k rerecordings. So, summary notes. 1) You may fade in character selection screen faster if you hold start. You didn't it. 2) Character name and type sets two things: sword max hits, and double coins chance. When you reaching sword max hits, it's reduced to 0 and does that long yellow swing. It deals higher damage to simple enemies, but at least for first boss in round 2 damage is same for yellow swing/normal swing. Double coins is a chance of 256 to get coins doubled. Sword max hits and double coins chance depends on same value: index. They both increase by index. So, the more you have sword max hits, the more you'll have double coins chance. This is not always working in opposite way. Just because gradation between chances is smaller. Index is in range 0..31. lowest sword max hits & luck is index 0, which has (3, 12) <- max hits, luck. so it has 12/256= 4.6875% chance to get coins doubled. Highest luck with same max hits is index 2 and it is (3, 16). The highest luck is 112 = 43.75%, and index is 31. Now, a small table: Sword max hits, highest luck, %:
3  16  6.250000
4  25  9.765625
5  37 14.453125
6  60 23.437500
7  72 28.125000
8  96 37.500000
9 112 43.750000
My test run has 9 max hits, and 112 luck. Names generated when you press the NAME button:
name     player 1        player 2
TARO 6  53 20.703125  6 57 22.265625
MOMO 6  54 21.093750  7 72 28.125000
YUM  5  33 12.890625  5 32 12.500000
LUCK 5  33 12.890625  4 23 8.984375
TODO 4  25 9.765625   6 53 20.703125
TAMB 6  58 22.656250  6 59 23.046875
BARU 5  35 13.671875  6 57 22.265625
JOJO 9 112 43.750000  8 80 31.250000
I suggest alternative names that have (9, 112) and nice meaning: GG (type GG and hit and hold START) GG?! GG!? There are many of ugly ones. I didn't check them all. Also, you have in the title "GENS TAS", but it doesn't look like GENS. Are you sure that you're using gens? Or does it mean Genesis? If it means genesis, then please don't confuse people, write Genesis or GEN or MD instead. Also, if your WIP is not on GENS, then you should not compare frames, because timings depends on emu. Info provided by me http://tasvideos.org/Addresses-121.html
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
TehBerral wrote:
I'm gonna feel stupid if it turns out that the fairies don't drop bombs on hard mode, but that is such a subtle difference I doubt that's the case. In any case, I haven't gotten the fairies to drop anything ONCE.
If I understand you correctly, you may start feel stupid right now. Because, difficulty is in fact the reason why you're unable to get any bombs from fairies. All following info is for (UE) [!]. There are exactly two different schemas for Hard, and Not-hard: 1) Hard: if you have low hp then it gives you 10000 additional score. if you have max hp then it gives you 30000 additional score. No random. 2) Not Hard: if you have low hp then it heals you by 10 hp. if you have max hp then: 50% chance to get 5000 additional score 50% chance to get additional bomb. In case you have max bombs then 100% first case (5000 score). So, basically, in hard mode there is no reason to touch them at all.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
is single ring in group after 2:41 missed or did you truly got it?
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
I would prefer having hotkey for saving this info into file. Or, having same/other hotkey for printing this info into console. Just because constant-writing to disk isn't good idea, because I don't know any app which able to refresh file runtime. and if you need to capture some moment, it will run away because of fps. in case if you need just single frame, you'll have to pause and seek for frame. similarly you can do same with those hotkeys. but with hotkeys you'll avoid spam in disk or console. if you anyways working in frame by frame mode, you can just add some separators into your console, to make things clear, and avoid using files again. For example:
Language: lua

print(string.format("===Frame %d", emu.framecount())) -- separation print(string.format("HP: %d X: %d Y: %d", HP, X, Y)) print(string.format("SpeedX: %d SpeedY: %d", SpeedX, SpeedY)) ....
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
OmnipotentEntity wrote:
It's because the kinetic energy is the integral of force over distance, rather than over time as you seem to be intuitively expecting.
Yes, this makes new flavor of understanding what exactly is conserved in "law of Conservation of Energy" It's kinda weird that this energy doesn't even depending on time. No matter what you do, no matter how fast you do, work is same. Just because of this defenition. From other hand, what is conserved is exactly THIS value, not other value.
p4wn3r wrote:
1) The fact that kinetic energy grows quadratically with velocity is a consequence of Newton's laws. Sure, you can look at the definition of work in terms of force and distance and derive kinetic energy from there, but that would simply shift the question to why work should be defined like that.
Yes, exactly, shift is to "why work should be defined like that?" My answer atm is: because what is conserved is exactly this thing.
p4wn3r wrote:
But again, it is a consequence of either Galilean invariance or Newton's laws, it is not true in relativity, because additivity of velocities does not hold in that theory.
I guess Newton's laws is enough. Galilean invariance is about conservations of laws itself. Using galilean transformation, kinetic energy changes. I'm unsure about total energy though. Anyway, my question was intended to crash misconceptions about what is energy, and energy conservations. I guess, I succeeded. But I can't grasp "valid" conception yet.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Sorry for interfering, but I have two very interesting questions. 1) Consider you have some object standing still. If you wanna increase its speed to 1 km/h you need to spend X energy. From now on, it's moving with speed 1 km/h and you need to spend Y energy to increase its speed to 2 km/h. From formulae of kinetic energy, we know, that it's mv2/2 which means, that Y > X Why Y > X? Why increasing of speed of moving object in same direction require more energy than standing still object? 2) Consider this object hits into wall, and all of its energy going into thermal energy. Considering same mv2/2 all going into thermal energy, it means that thermal energy after hit of object at speed 2 km/h is 4 times more than thermal energy after speed 1 km/h. Does it mean, that temprature also 4 times more?
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Warp wrote:
This seems paradoxical. The universe could end before the photon reaches its target, yet from its own perspective it reaches the target in 0 seconds.
You just underestimating rate of expansion in your statement. I don't know how to explain it. But expansion rate is close to adding same length as being passed in single turn. Intuition tells to some people that light should not reach end at all. Your intuition is completely opposite: you're thinking about speed of light, and how short distance is. So, none of intuition above works well. But first one is close to real situation. Trick here that any move towards end, is changing ratio of passed distance to total distance. In other words, it increase progress in percents. It isn't proof either, because it may grow infinitely long, and never reach 100% as in case when each second space extends in twice.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
feos wrote:
https://en.wikipedia.org/wiki/Jon_Burton
Didn't know, but anyway: 1996 Sonic 3D Blast Programmer
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
I don't like only name. If it's director's cut, then it's author should be "director". If it is indeed director's vision of game, then ok. But if it's just ideas of some hacker of original game, it should not confuse people that is is director's vision of game.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Suddenly, sometimes discrete is harder than continious. Here is solution for continious task: Assume photon starts from coord 0 and travels to coord 100. Then, Norm is changing ||x|| = |x|*(1+t). i.e. t = 0: 1+t = 1 t = 1: 1+t = 2 t = 2: 1+t = 3 So expansion same as in task. Then, speed in terms of coords is 1/(1+t), and time it will take is integral of 1/(1+t) dt from 0 to x. So, equation is: log(x+1)-log(1) = 100 log(x+1) = 100 e^log(x+1) = e^100 x+1 = e^100 x = e^100 - 1 Here, and everywhere above, log is natural logarithm.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Script updated, link is same. Aim shows where cursor aims. Grid is grid. Fog shows visibility border. Turrets is turrets range. Sight is unit explore range. Range is unit weapon range. This shape of "circle" is not bug, it's actual shape of distance. Unit may hit unit if it has center inside that shape. Unit may hit building if it has cell center inside that shape. Turrets center of range is left top corner. By the way here is some doc http://ledmeister.com/dunexref.htm I don't know its validity.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Warp wrote:
Famously, lim(x → ∞) (1 + 1/x)x = e which is one of the definitions of e. However, it seems that also: lim(x → -∞) (1 + 1/x)x = e although it's not immediately apparent why.
lets y = -x, then lim(x → -∞) (1 + 1/x)x = lim(y → ∞) (1 - 1/y)-y = = lim(y → ∞) ((y - 1)/y)-y = lim(y → ∞) (y/(y - 1))y = = lim(y → ∞) ((y - 1 + 1)/(y - 1))y = lim(y → ∞) (1 + 1/(y - 1))y = = lim(y → ∞) (1 + 1/(y - 1))(y - 1) * (1 + 1 / (y - 1)) = e * lim (y → ∞) (1 + 1/(y - 1)) = e
FractalFusion wrote:
Since gr(α)=C+1 and gr(x)=x+n/x is increasing on [sqrt(n),∞), it follows that gr(ceil(α))≥C+1, so g(ceil(α))≥C+1. If ceil(α)-1≥sqrt(n), then gr(ceil(α)-1)<C+1, and so g(ceil(α)-1)=C. (If not, then ceil(α)-1=floor(sqrt(n)), and it must follow that g(ceil(α)-1)=C; otherwise min(g(x))=C+1, contradicting that the minimum is C.) Therefore the minimum d such that g(d)<g(d+1) is d=ceil(α)-1.
First, just to make it clearer: gr(ceil(α))≥C+1 is because gr(α)=C+1 and gr(α) is non-decreaseing in range [a, ceil(a)]. Now, "If ceil(α)-1≥sqrt(n)". To make clearer. Not taking into account this condition above, there are two possible cases: 1) ceil(α-1)<sqrt(n)<=ceil(α) 2) sqrt(n)<=ceil(α-1)<ceil(α) This "If ceil(α)-1≥sqrt(n)" is about case (2). "then gr(ceil(α)-1)<C+1" Reason is following: gr(α-epsilon) < gr(α) = C + 1 In other words: gr(α-epsilon) < C + 1 for small epsilon, including epsilon = α - ceil(α - 1) Thus g(ceil(α)-1) <= C because it is flooring. Ok, this is fine. Now case (1): ceil(α-1)<sqrt(n)<=ceil(α) "If not, then ceil(α)-1=floor(sqrt(n))," Indeed. "and it must follow that g(ceil(α)-1)=C; otherwise min(g(x))=C+1, contradicting that the minimum is C." Yes, because ceil(a) then floor(sqrt(n))+1, and min(g(x)) should be among floor(sqrt(n)), and floor(sqrt(n))+1. Okay. Great, I haven't find any mistakes. Just to make you know, my approach was following: Start: floor(n/x) = floor(n/(x+1)) Now, from definition of floor: floor(n/x) <= n/x < floor(n/x) + 1 floor(n/(x+1)) <= n/(x+1) < floor(n/(x+1)) + 1 Now you can replace floor(n/(x+1)) by floor(n/x) because they should be equal. floor(n/x) <= n/x < floor(n/x) + 1 floor(n/x) <= n/(x+1) < floor(n/x) + 1 now, you can multiply both sides of both inequations because they works well on whole real range. floor(n/x)*x <= n < (floor(n/x) + 1)*x floor(n/x)*(x+1) <= n < (floor(n/x) + 1)*(x+1) now, using intersection of this ranges, we reduce this into one inequation: floor(n/x)*(x+1) <= n < (floor(n/x) + 1)*x Open brackets: floor(n/x)*x + floor(n/x) <= n < floor(n/x)*x + x Now, subtract floor(n/x)*x from all sides. floor(n/x) <= n - floor(n/x)*x < x Stare at it. Notice, that n - floor(n/x)*x is remainder. In other words: floor(n/x) <= (n mod x) < x it means, that right "<" is always true because (n mod x) is always < x. Now we have other equivalent task: floor(n/x) <= (n mod x) From here we can easily prove that lower bound of x is sqrt(n) because if x < sqrt(n) then: sqrt(n) < floor(n/x) ??? (n mod x) < sqrt(n) -->> floor(n/x) > (n mod x) So, lower bound is proved by contradiction. Now, next my idea was to use following, define m = floor(sqrt(n)). now m*m <= n < (m+1)*(m+1) define also k such as: m*m + k = n then look at following: m^2 + k = m^2 - y^2 + y^2 + k = (m + y)*(m - y) + y^2 + k = n So, I was thinking what I can say about such: (m + y)*(m - y) + y^2 + k = n Then to have floor(n/(m+y)) = m - y we need to have y^2 + k < m + y, and we also should fit floor(n/x) <= (n mod x): m - y <= y^2 + k So, answer can be m + y if: (y ^ 2 + k) < m + y and m - y <= y^2 + k Here possibly mistakes because I just wrote this from top of my head. But from here you can also see that this grows by power of two, so y should be ~ sqrt(m) where m ~ sqrt(n). I haven't finished this approach. you did first.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
OmnipotentEntity wrote:
r57shell wrote:
FractalFusion wrote:
Indeed, g(x)=x+f(x), and so g(d+1)-g(d)=1+f(d+1)-f(d).
Тhis works only for x from Z (integer).
QED. Considering that g is defined to have a domain of ℝ, nothing in these steps requires d ∈ ℤ.
f(x) = floor(n/x) g(x) = floor(x+n/x) Let x = 1/2, then f(1/2) = floor(n/(1/2)) = floor(n*2) = n*2 g(x) = floor(1/2 + n/(1/2)) = floor(1/2 + n*2) = n*2 x+f(x) = 1/2 + f(1/2) = 1/2 + n*2 (for f(1/2) look a bit up) g(x) != x + f(x) n*2 != 1/2 + n*2 FractalFusion, I'll look proof a bit later.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
FractalFusion wrote:
and the integer-valued functions f(x)=floor(n/x) and g(x)=floor(x+n/x).
Just wanna confirm that they are both Z->Z (integer -> integer), because:
FractalFusion wrote:
Indeed, g(x)=x+f(x), and so g(d+1)-g(d)=1+f(d+1)-f(d).
Тhis works only for x from Z (integer).
FractalFusion wrote:
which mean g(x) is weakly decreasing up to x=k and weakly increasing from x=k+1 onward.
It's true, just wanna add comment: we don't know difference between g(k) and g(k+1).
FractalFusion wrote:
Let C=min(g(x)). The minimum d such that g(d)<g(d+1) occurs at ceil(α)-1, where α is the larger real solution to x+n/x=C+1.
This is out of space. This is where rigorous proof ends. If here would be also proof, that it is indeed occurs at ceil(a) -> it will be truly rigorous.
Experienced Forum User, Published Author, Player (97)
Joined: 12/12/2013
Posts: 376
Location: Russia
Easy yes vote. I was thinking to write about trick you haven't show anywhere. But, you did it at almost end (water level): if you have invincibility you can fall infinitely.
1 2
6 7 8
15 16