I'm hooking CreateFile and MapViewOfFile.
Sorry, I was mistaken when I wrote that. I'm not actually hooking MapViewOfFile, but I'm hooking CreateFileMapping.
Here's a general overview of what I'm doing on the client end:
I hook CreateFileA and in my function that gets called instead of CreateFileA, I parse the filename and see if it contains .mul.
If it contains .mul, I change the desired access from read only to read/write. I change the sharemode to 0x3 (fileshare read/write)
I then hook CreateFileMappingA (I wrote the wrong one in my post above).
I get the filename to parse, (you can see how to do this here:
http://msdn.microsoft.com/en-us/library/aa366789(v=vs.85).aspx)
Be sure to unmap the view you make when getting the filename, and clean things up.
After I have obtained the filename, I parse it again. If it contains static*.mul, I call CreateFileMappingA and pass it INVALID_HANDLE_VALUE. This causes the mapping to be put on the system paging file. I give it a size that is 1.5 bigger than the original statics file. Then I copy the contents of the original static file to the memory map I created and I pass it back to the client. Now the client has a view that is larger than the original file, but its index doesn't point to any bytes that are beyond the end of the original file. When I intercept one of the two update block packets, I check to see if the new statics size is larger than the existing one. If it is, I write the new block to the end of the filemap, and I update the index. I also write the change out to the original statics file (which isn't mapped at this point). This causes fragmentation in the original statics file (spaces of data that are not being used). To resolve this, I plan to defragment them periodically. To do this I will just read the index file block by block, building a new file with all the blocks in order and the fragments gone.
If the file isn't a statics file, I simply call CreateFileMappingA with flProtect set to 4, and I set the lpName to the filename.mul and I append the Process ID to the end of it. (This allows me to find the mapped file in Razor, and it can be found for multiple clients because they have different process IDs)
After I receive a block update, I update the corresponding portion of the file maps. Then I need to force the client to refresh. Now this took me a long time to figure out. In fact the entire project depended on being able to force the client to update at an arbitrary time. To figure it out, I did a lot of searching with OlyDbg and Ida. I received some help from
www.JoinUO.com, they helped me find the packet tables and some other very useful things. Up until just recently I was creating a remote thread to call a specific function inside the client, but I found a better way.
To force the client to refresh, all you have to do is send it a reject movement packet (0x21) passing zero as its sequence number and pass it a valid location for the player. Then immediately after, pass the client a move acknowledge packet (0x22) with a sequence number of 0, and a status of 0. I do this from razor immediately after I receive a block update. (I actually send both packets in a clump) In this way I don't have to send any extra packets from the server. I'm assuming you would send these packets from your local proxy.
The result is that the client updates its local cache and you see your changes immediately on screen. Now you may see your screen flash black for part of a second when it does this, but that's a small price to pay for the functionality.