Log In  

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

P#121896 2022-12-06 04:56 ( Edited 2022-12-06 05:05)

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 ?

P#121897 2022-12-06 05:38

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.

P#121898 2022-12-06 05:53
2

@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.

P#121902 2022-12-06 10:53 ( Edited 2022-12-06 14:58)

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!

P#121926 2022-12-06 17:28

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.

P#121927 2022-12-06 17:39
4

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.

P#121929 2022-12-06 17:57

@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

P#121937 2022-12-06 18:22
1

@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.

P#121940 2022-12-06 18:46

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.

P#121942 2022-12-06 18:54

@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!

P#121943 2022-12-06 19:05

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2023-01-28 04:14:42 | 0.049s | Q:20