Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Wednesday, December 10, 2008

Plugins for the PSP

I've been working on a pet project of mine that I thought would be fun to do. I've been able to write and compile code for running on custom firmware on the PSP for a while now, but I haven't decided on what to do.

I've wanted to make a decent game, perhaps a bike-physics game clone of the one on teagames that I enjoyed so much playing during dull hours at the office, or a tower defense game with nice graphics and interesting weapons.

I've finally got more or less of a hang on the use of makefiles, enough that tweaking them a bit doesn't faze me like it used to. I don't exactly bother to write one from scratch though, as there are still some things I don't fully understand.

I've also enjoyed using plugins, which are really fantastic as it really lets you do so many more things with the PSP than what it can out-of-the-box.

So now I've tried to write my own plugin, based on an existing one, but with different features. It's been considered by some extremely difficult to put folders for games in the XMB. And I wholly agree.

But I've come up with an idea that might work.

When you place a folder in your /PSP/GAME folder that doesn't contain an EBOOT.PBP (the executable) , the XMB will display it as "Corrupted Data". This is just the way it's designed, zero levels deep.

There are plugins that allow you to categorize your games, and by placing them in different folders, you can select which category is currently active, and it displays only those games in the xmb.

These plugins use a button combination or a menu to select the category.

I thought of a way to let a folder show up in GAME, by spoofing an EBOOT when the firmware is trying to read the name and icon for the EBOOT.

The firmware tries to load an EBOOT for each folder it sees in /PSP/GAME. In the empty folder, it tries to load an EBOOT.PBP and as there is none, it fails and tells the firmware that the game is probably corrupt.

With my plugin in place, the code fails, but the plugin catches it and instead generates a fake EBOOT on the fly using the name of the folder and a default icon stored somewhere accessible, and tells the firmware everything is OK, don't worry, there is an EBOOT here.

Right now this is all the plugin does - it just allows folders to be displayed as EBOOTs in the XMB.

[screenshot goes here]

Now comes the hard part - intercepting when the user tries to open my fake EBOOT.

Normally the firmware tries to load the game, displaying the gameboot intro before launching the game.

What I need to do is catch the keypress before the firmware does, figure out what EBOOT the user is pointed at, check if it is one of mine, and if so, cancel loading the EBOOT and switch "categories" - folders really - and flush the memory stick cache.

This is where it gets interesting.

The PSP firmware is made up of several modules that load other modules as needed. The vshmain module (?) is the module that displays the top-level menu, the XMB.

When you select a top level item, vshmain (?) loads the approriate module - game_plugin in my case.

game_plugin is now responsible for reading the memory stick, drawing the icons, handling the menu. I'm not sure if it is entirely responsible though, it probably makes calls to other existing modules.

I makes sense that the game_plugin module contains a subroutine for checking if the user has pressed a button, or acting on button presses. What I need to do is patch the module to jump to my own code for checking button presses, before I call the original code.

As of now, I only have a fleeting understanding of how this can be done. With my previous knowlege of patching ROM code, I have some idea, but this is a 32-bit RISC platform with relocatable code, making things much more difficult for someone from a 8/16-bit fixed code background. There is probably some mechanism to locate the module's base entry point, and offset from there to some function.

I don't know how to find that function yet. But I have some ideas.

Learn MIPS assembly.

The module loads gameboot.pmf prior to launching the game. Look for the string gameboot.pmf, and to the code that references it and backtrack until you can find where the button presses are. Hopefully button polling/testing can be easily identified in assembly. (HAH!) NOPping out code is a time-honored method of isolating code.

Patch the code somewhere appropriate to jump to a function in my plugin.

This function needs to know what EBOOT the game_plugin module is currently "looking" at. Even after finding the right place to patch and patching the code, this is much more difficult to do. Perhaps the module's memory space contains an array of paths, and an index to the current entry.

Or perhaps there is a function that does this. With the caching that the XMB implements, this makes things much more difficult. Perhaps recoding a game_plugin is easier in the long run.

One thing fir sure, I would have learned a lot more by the time I'm done with this project, whether it is complete or not.

Wednesday, October 1, 2008

Returning Strings from WinAPI in VB.Net

I know this has been covered a lot in other places, but the things is, all their examples have failed to work for me. Here I offer another method, one that worked for me.

Declare Auto Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal lpString As String, ByVal cch As Integer) As Integer

This is the usual declaration of the API function GetWindowText. As we know lpString should be marshalled as a LPCSTR, but according to this article, VB.Net doesn't allow you to specify the marshaling attribute on parameters, and purportedly the above declaration works just fine.

When I tried this version of the API call, using GetWindowTextLength to determine the length of the string to be filled with spaces before passing to GetWindowText, I ended up with a bunch of non-ASCII characters. When I tried using the MarshalAs attribute, all I got was an empty string of the "correct" length.

After some exasperation I decided that since the API was expecting a pointer to a string, I might as well give one, or the closest to one, a byte array. The declaration was then:

Declare Auto Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal lpByteArray As Byte(), ByVal cch As Integer) As Integer

Then:

...
Dim Ret As Integer, sByte As Byte()
Ret = GetWindowTextLength(hwnd)
If Ret > 0 Then
ReDim sByte(Ret + 1)
lret = GetWindowText(hwnd, sByte, Ret + 1)
Return System.Text.Encoding.ASCII.GetString(sByte)
End if
...

Just as a sanity check (and before I found the Encoding.ASCII.GetString function), I stepped through the function to see what was being stored in sByte. They looked to be ASCII-flavored bytes (mmmm) so I looked for a function to convert them to a string, et voila!

I was actually expecting to be required to pass the first member of the byte array as a parameter to GetWindowText, but apparently .Net did the math and converted the byte array into a pointer of sorts.

I must admit I'm a bit new to .Net (two years of actual usage) and newer to managed and unmanaged code mixing, so I can't tell you that this is the right way to do it, but this method worked where others failed.

I'm on .NET 2003, in case you were wondering.

Wednesday, January 30, 2008

Some Sharepoint Stuff

In my real job, I do a bit of Sharepoint support, not much development, so I don't really get to work with the dotNet side of things.

I do get to work with the database, which gives quite a bit of insight on how Sharepoint works internally.

One of the things stored in the Sharepoint database is the individual settings for each webpart.

Now, our client has a Sharepoint based site, with some custom dotNet stuff thrown into the defaut.aspx of the template's web Homepage. This Homepage really gives us headaches because some users have the ability to close webparts. This could be easily fixed by getting into the Modify view, but something with the additional code prevents Modify view from working.

What happens is when you place the web in Modify mode, the browser gets stuck and stops responding, and CPU usage goes up. I've debugged it and partially traced it to an infinite loop in the resize functions in Sharepoint's Javascript code.

I'm not too eager just yet to go messing around with the dotNet code, as some loaded controls might be involved in messing up the page's DOM heirarchy or something that could cause an infinite loop.

To restore the missing webpart, we have a backup default.aspx of the vanilla Sharepoint web Hompage which we use to temporarily replace the dotNet-messed-up one.

With the vanilla page in place we can safely call Modify mode and restore the webpart.

Now I was asked how we could prevent users from closing the webpart accidentally, and one of the things that came up was to disable the close button. This can only be done in Modify mode, which requires the vanilla page, which requires that no one is using the site, which can only be done during non-business hours.

With a global user base, and a shift nearly opposite to the business users, the window where we can use the vanilla page to do our stuff is only 2-3 hours. Coupled with the fact that there are hundreds of sites to change, it begins to be impractical.

So with some determination I set out to find where Sharepoint stored the settings for webparts, and the field tp_AllUsersProperties seemed to invite further investigation. It's an image field, used to store an arbitrarily large binary value. Here is what I have so far:

"Header" - Always present: Doesn't seem to change
0105 0000 0002 2A00 4A00 2900 033D 0000 022A 0049 0029 0003 3E00 0001

"Separator" - Seems to separate entities
0F01

"Close Disabled" - Present if "Allow Close" is unchecked.
15 0029 0003 4800

"Zone Change Disabled" - Present if "Allow Zone Change" is unchecked

2B 0029 0003 4800

"Minimise Disabled" - Present if "Allow Minimise" is unchecked.

16 0029 0003 4800

Description - Text
02 0029 0003 FFFF nn

nn is the length of the text in bytes, no null termination

Detail Link - URL
1A 0029 0003 FFFF nn

nn is the length of the text in bytes, no null termination

Help Link - URL
1B 0029 0003 FFFF nn

nn is the length of the text in bytes, no null termination

Icon (Small) - Image URL
1F 0029 0003 FFFF nn

nn is the length of the text in bytes, no null termination

Missing Assembly - Text
27 0029 0003 FFFF nn

nn is the length of the text in bytes, no null termination

Icon (Large) - Image URL
20 0029 0003 FFFF nn

nn is the length of the text in bytes, no null termination


"EOF" - Always appears at the end

0F0F

It only seems to encode the Advanced Settings.

If it is null, default settings are used when in Modify mode.

If no text entities are defined, there must be at least one text entity before the EOF marker. The entity can be any text entity such as Detail but it should be truncated as:

1A 0029 00

each entity is followed by the separator, unless it is the last entity, where is will be followed by the EOF marker.

If entities have an identifier (first byte), a robust parser should be able to read the entities even if they were written in any order, unless it expects an order in which they are written.

nn is only a byte wide so the maximum number of characters in the text entities is 255.

Having incorrect values results in an error with Sharepoint not being able to "serialize" the webpart.

I have no idea what the recurring values "0029 0003 FFFF" mean. Changing them results in the same error as above.

I haven't tried this on another Sharepoint, or across multiple sites.

You should join tp_PagerUrlId to the Docs table in order to find the page that the webpart is on.

tp_WebPartId ias actually a GUID of a hash of the webpart name. See about that here.

As we are looking to eliminate the ability to Modify I've since moved on to more simpler Javascript to hide the webparts Modify buttons.

Anyway, I hope this is useful to somebody.

Wednesday, January 23, 2008

PSP-Dev Woes

Today was by far the most painless install of the psp toolchain I ever did. While this is only the second time, and I hope I won't have to do it again soon, I did learn a few things along the way.

The first thing I did was to jot down exactly how I installed the toolchain and what problems popped up, so I wouldn't have to rely on my now-rusty memory.

It helped bucket loads that I was using Dan Peori's toolchain/library installers. It took a while but when everything was done, most of the source code I had on me was compiling without a glitch.

I wasn't supposed to go back to trying to code for the PSP. I wanted very much to give up on it, since I couldn't think of anything useful to begin to write and start learning that PSPDEV-lore. Everything about programming for the PSP seems so complicated and it's hard to get your hands on some easy-to-understand documentation.

I was also beaten to the Multi-Update folder idea, something that allows you to switch the contents of your update folder, as only one update folder is allowed on your PSP.

A tower defense game has already been made. It would be nice to make one with nice graphics though. At this point it thinking about developing for the PSP brings me back to SDL, as there is a port available for the PSP, it would be so much easier to develop on the PC, work out the main bugs, then port it to the PSP.

Maybe I should go back to square one, write a bouncing ball app, and move on from there.