Hi all! I have a problem. I made a game and I've lost the source code (my laptop was stolen and my code wasn't backed up 'cause I'm a dummy).
Regrettably I didn't post it to BBS either :/
I'd like to be able to recover the .p8 file from the exported html and js files. Is there any way to do this?
Many thanks <3
You won't be able to convert HTML/JS back to true .P8 text file, however, you are saying your laptop was stolen ?
That's a bit more serious than losing your source-code as it could also contain personal + credit card information.
Have you let the proper authorities know about this, @waskerdu ?
Also are you in a position to code in Pico-8 today minus your laptop ?
I appreciate the concern! The laptop was stolen a while ago and I took all the necessary precautions re my private data.
That's a bummer though. I was hoping there might be a way to decompile the js file even if the variable/function names were garbled. Thanks for your answer.
@dw817 are you sure it can't be converted back? The js file contains a section clearly marked as the cart data, and I suspect (given the interpreter-based nature of pico-8) that the cart data is the only part that's different in each export. I have errands to run so I can't try it myself at the moment (errands to run), but I would expect there to be a way to stuff that cart data into a png file that would allow it to be loaded normally.
Edit a couple hours later: Okay, finished what I needed to do and tried it out, since I was really curious. The data labeled as "cartdata" in the exported js file from an HTML export is in fact the same as a p8.png file with one important difference. The version and platform numbers apparently aren't included, and neither is the hash for checking for corruption. I think this is understandable, since they're not really needed in a save that isn't meant to be reused with variable players. The solution is to just add in some extra values for version and platform then 0's for the rest of the available space.
That said, I did succeed in converting an existing cartridge from the js cartdata (copy-pasted into a text file) into a png file that pico-8 could accept. I now have a windows executable that can a txt file can be dragged onto to do the conversion. Would you like me to send it to you? And if yes, how and in what form? The code is in c and just needs some basic libraries and the reference png library, and I can probably export to linux if needed. Alternatively, if you post your js file, I can just send you the converted cartridge.
Wow! That's exactly what I was looking for. My js file is here
I would love to see your code too! Thanks so much for your help!
Hmm ... Now @kimiyoribaka. Are you certain ? If so that will be the recovery program of the century.
Likely others have lost their sourcecode for Pico-8 too in one way or another.
Your recovered file is here . I opened it after converting to make sure the conversion succeeded and also to re-save it with the proper cartridge image and version data. My conversion code doesn't add the actual cartridge image since that's not needed for reading the data.
The source for the converter, after cleaning a bit of the mess from debugging, is this:
#include <string.h> #include <stdio.h> #include <stdlib.h> #include "png.h" typedef struct pixel { char a, r, g, b; } pixel; int main(int n, char* args[]) { int i, len, t; for (i = 0; i < n; i++) { len = strlen(args[i]); char ext[5]; memset(ext, 0, 5); memcpy(ext, args[i] + (len - 4), 4); if (!strcmp(ext, ".txt")) { t = 1; break; } memset(ext, 0, 5); memcpy(ext, args[i] + (len - 3), 3); if (!strcmp(ext, ".js")) { t = 2; break; } } if (i == n) return 0; char* outname = malloc(len + 5); memset(outname, 0, len + 5); if (t == 1) { memcpy(outname, args[i], len - 4); memcpy(outname + len - 4, ".p8.png", 7); } else { memcpy(outname, args[i], len - 3); memcpy(outname + len - 3, ".p8.png", 7); } FILE* f = fopen(args[i], "rb"); int c; int ci; char s[13]; memset(s, 0, 13); int sn = 0; pixel p[160 * 205]; memset(p, 0, 160 * 205 * 4); int pn = 0; if (t == 2) { fgets(s, 13, f); char key[] = "var _cartdat"; while (strncmp(key, s, 12)) { for (int ti = 0; ti < 12; ti++) s[ti] = s[ti + 1]; s[12] = 0; s[11] = fgetc(f); } sn = 0; memset(s, 0, 4); } c = fgetc(f); while (c != EOF && pn < 160 * 205 && c != ']') { if (c >= '0' && c <= '9' && sn < 3) { s[sn] = c; sn++; } else if (c == ',') { ci = atoi(s); p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; pn++; memset(s, 0, 4); sn = 0; } c = fgetc(f); } if (pn < 160 * 205) { ci = atoi(s); p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; } pn = 0x8000; ci = 34; p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; pn = 0x8001; ci = 0; p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; pn = 0x8002; ci = 2; p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; pn = 0x8003; ci = 5; p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; pn = 0x8004; ci = 0x77; p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; pn = 0x8005; ci = 0x02; p[pn].a = (ci & 0xC0)>>6; p[pn].r = (ci & 0x30)>>4; p[pn].g = (ci & 0xC)>>2; p[pn].b = ci & 0x3; fclose(f); char* pixp = (char*)p; png_image image; memset(&image, 0, sizeof(image)); image.version = PNG_IMAGE_VERSION; image.width = 160; image.height = 205; image.format = PNG_FORMAT_ARGB; png_image_write_to_file(&image, outname, 0, pixp, 0, NULL); return 0; } |
I added the ability to accept js files as input later on, but I kept the txt input just in case. The way it can tell where the data is in the js file is by checking for the variable name and assuming the data is declared immediately. Other than that, all it's doing is converting the human readable numbers to bytes, inserting the bits into the correct spots in the pixel data, then applying the simplified png api that the reference library comes with. The big section in the middle that directly changes data is for adding in the version numbers and pretending the platform is "windows" so the engine doesn't get confused.
@kimiyoribaka you. are. AMAZING. I tried poking around in the js file but I didn't get very far. Where did you learn these forbidden secrets?
Either way, thank you so much. I'm in tears (and feel very inadequate as a programmer). I wish I could do something for you. Have a marvelous day <3
@waskerdu
The format for the .p8.png files is on the wiki:
https://pico-8.fandom.com/wiki/P8PNGFileFormat
Realizing that the _cartdat variable is source code is a lot easier when you know how interpreter engines work. In my case, I have a lot of experience poking around various engines. I've even made my own, but it's not as immediately fun to use as pico-8.
@dw817
Remember that pico-8 is designed with preserving source code in mind. It also makes things much much easier when supporting multiple platforms. As for other people losing their source, there's probably some way to recover the code as long they have a copy in some form. However, if it's a standalone binary, that'd probably be harder since the .pod format isn't as straightforward. I suspect it could be figured out by comparing a raw byte export using a hex editor though.
Yet you can recover from JS format. Exceptionally well done, @kimiyoribaka. All I can give you is a star but it is most deservedly so.
Now if you can make an online tool out of this that would be one step closer to helping others recover their lost P8 and PNG source-code in the future.
@kimiyoribaka I've made a virtual console too! It's in a rough state rn but it's mine. I'd love to see yours!
Edit: ...and the post I was responding to has disappeared. I don't have the internet social experience to know what the normal thing to do in this case is.
@tabreturn
"a.out" is a common default executable name for linux (including macOS, potentially, though I think that's less common). If you're on linux, you should be able to run that file in a command line (AKA terminal). The exact method would depend on which linux distro you're using and how it's configured though. Unfortunately, I primarily work on windows, even despite having a dual-boot setup, simply out of preference, so my knowledge of linux is limited. My guess is this command would work:
./a.out pico8game.js |
of course replacing "pico8game" with your actual js file.
If you're not on linux, then I don't know why you got a file with that extension. Though, if you're using gcc and you'd rather get a different filename, then the command line parameter is -o new_filename
.
One other thing, if you're using an OS other than windows, the byte order could be wrong in the above code. If that is the case, then it should work to just reverse the order of the variables in the pixel struct.
Thanks, @kimiyoribaka! I've worked it out. Appreciate the pointers :)
[Please log in to post a comment]