On the Vita, there are additional limitations on the PSP emulator that add a few constraints, which I will describe here.
- a menu for VHBL is a "regular" EBOOT.PBP file. You install it by simply replacing the EBOOT.PBP file included in VHBL
Example
HBL comes with an open source example that you can find here:
http://code.google.com/p/valentine-hbl/ ... der%2Fmenu
This does not have any vita specific enhancements, though.
HBL API
Your Menu must follow an API that has been defined a long time ago in HBL.
HBL passes a pointer to a struct that is described as follow:
Code: Select all
typedef struct
{
unsigned long APIVersion;
char Credits[32];
char VersionName[32];
char *BackgroundFilename; // set to NULL to let menu choose.
char * filename; // The menu will write the selected filename there
} tMenuApi
Code: Select all
void * ebootPath;
int main(int argc, char *argv[])
{
char dummy_filename[256];
strcpy(dummy_filename, "ms0:/PSP/GAME");
int settingsAddr = 0;
if (argc > 1) {
char * hex = argv[1];
*(hex + 8 ) = 0; //bug in HBL ?
settingsAddr = xstrtoi(hex, 8);
}
if (settingsAddr) {
tMenuApi * settings = (tMenuApi *) settingsAddr;
ebootPath = (void *) settings->filename;
} else {
ebootPath = dummy_filename;
}
The command we write is usually the path to an eboot (to run a homebrew), but can also be the string "quit" if we want to ask HBL to qui back to the XMB:
To quit:
Code: Select all
strcpy(ebootPath, "quit");
sceKernelExitGame();
Code: Select all
strcpy(ebootPath, "ms0:/PSP/GAME/TEST/EBOOT.PBP");
sceKernelExitGame();
That's it for the basics. Below are some Vita specific rules
Vita specifics
. and .. do not exist
For some reason, retrieving "." and ".." with conventional "dread" calls does not always work with VHBL on the vita (although recent versions attempt to fix this, so it might be worth trying). This can be a problem if you are trying to create a list of files in a folder, and if you want to give the user a possibility to browse folders (something that a VHBL menu might want to do, especially since we also want to be able to access the SAVEDATA folder, see below).
This is how I managed to handle this case in wMenu (C++ code)
(note: this will not compile, it is taken out of context, so don't copy paste blindly)
Code: Select all
void GameApp::loadFiles(const char * folder){
base = folder;
if (base[base.size()-1] != '/') {
base.append("/");
}
DIR *mDip = opendir(base.c_str());
if (!mDip)
return;
int count = std::count(base.begin(), base.end(), '/');
if (count > 1)
homebrews.push_back("..");
while ((mDit = readdir(mDip))){
if (strcmp(mDit->d_name, ".") == 0) continue;
if (strcmp(mDit->d_name, "..") == 0) continue; //added above
[...]
In order to stay compatible with regular readdir function (if the menu is running on a regular psp which doesn't have the limitations of the vita, for example), I also force remove "." and ".." from the readdir results, in case I find them
Extracting zips
Because CMA does not allow us to copy folders and subfolders, the best way I found to install homebrews on the Vita is to put them in a zip file, that gets extracted by the VHBL menu. In order to simplify my code I only support uncompressed zip files, but it is up to you to make things "better".
[TODO. My own code is a mess and cannot be used as an example. Anybody knows of a simple zip library and could give an example?]
The code below shows how I "guess" where to extract the zip
Code: Select all
//Load the list of files in the install zip if they're not loaded yet
void GameApp::ensureExtractFilesList()
{
if (extractError)
return;
if (!zipFiles.empty())
return;
ZipSupport::PreloadZip(gameToExtract.c_str(), zipFiles);
currentZipFile.open(gameToExtract.c_str(), ios::binary);
extractFolder = MEMSTICK_ROOT;
//Look for eboot and guess output folder from there.
for (map<std::string, ZipSupport::limited_file_info>::iterator it = zipFiles.begin(); it !=zipFiles.end(); ++it) {
string name = it->first;
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
if ((name.find("eboot.pbp") != string::npos) || (name.find("wmenu.bin") != string::npos)) {
std::stringstream out;
out << MEMSTICK_ROOT;
int count = std::count(name.begin(), name.end(), '/');
switch (count) {
case 0:
{
// eboot.pbp
int rnd = rand()%10000;
out << "PSP/GAME/WMENU_" << rnd << "/";
break;
}
case 1:
//mygamegame/eboot.pbp
out << "PSP/GAME/";
break;
case 2:
//game/mygame/eboot.pbp
out << "PSP/";
break;
case 3:
//psp/game/mygame/eboot.pbp
break;
default:
extractError = 1;
currentZipFile.close();
}
extractFolder = out.str();
return;
}
}
currentZipFile.close();
return;
}
A limitation of the PSP emulator on the vita is that it is not possible to create a file named EBOOT.PBP under the GAME/*/ folder. To bypass this limitation, wMenu renames all files named EBOOT.PBP into wmenu.bin. Technically, any name is fine as long as it is exactly 9 characters long (this is a constraint from HBL).
This is what my code looks like:
Code: Select all
string outputfile = it->first;
string lcoutputfile = outputfile;
std::transform(lcoutputfile.begin(), lcoutputfile.end(), lcoutputfile.begin(), ::tolower);
size_t found = lcoutputfile.find("eboot.pbp");
if (found != string::npos)
outputfile = it->first.substr(0, found) + "wmenu.bin" + it->first.substr(found + 9);
string outputFile = extractFolder + outputfile;
Remember, this also means that you will have to copy the right filename (ms0:/PSP/GAME/TEST/wmenu.bin for example) in "ebootPath" above when running the homebrews!
That's basically it. for the rest, your imagination is the limit.

