Ability to load all types of uncompiled assets from datastore directory?

Currently it's possible to load both uncompiled and compiled files from the datastore directory. I find it a bit confusing which ones are allowed and which are not. I know it's not arbitrary, but it can feel that way.

I tried to load a PNG from my datastore directory and failed.

uncompiled

  • MP3
  • JSON

compiled

  • PDA (audio)
  • PDI (image)
  • PDT (imagtable)

Given that user-created content would be placed in the datastore directory I am wondering if it would be possible for one or more of the following:

  1. uncompiled content to be loadable from datastore
    • PNG
    • GIF
    • WAV
    • etc
  2. files to be compiled on device somehow
    • after unmounting/restarting Playdate if any have been added?
    • on demand by game on first access?
    • some other way
  3. an easier way to compile individual assets than using pdc (which works on directories and produces .pdx)

Thoughts appreciated.

1 Like

related Discord:

https://discord.com/channels/590783499494228008/712709706984194178/745028644749181019

[ 22:17 ] Dave :bust_in_silhouette: :
You can save a sample.. but only to WAV format, which can't then be read back in without a pass through the compiler. ugh.
[ 22:19 ] Dave :bust_in_silhouette: :
The data formats are the same, they just have different headers. We could either give you the option of which format to save to, or allow loading wav files as long as they're in a format we can handle (not 24 bit, not compressed). Or both!(edited)
[ 22:22 ] Toni (CET) :
I would be happy with either solution :slight_smile:
[ 22:23 ] Dave :bust_in_silhouette: :
for now I'll hack together a Lua function to convert a wav to a pda. one sec!
[ 23:06 ] Dave :bust_in_silhouette: : Here you go! Press A to record a 2 second sample, then B to play it back:

function wav2pda(filename)
    local fil = playdate.file
    local wav = fil.open(filename, fil.kFileRead)
    local datalen = fil.getSize(filename) - 44
    local pda = fil.open(string.sub(filename,0,#filename-4)..".pda", fil.kFileWrite)
    
    wav:seek(44)
    pda:write("Playdate AUD\68\172\0\2") -- 44kHz 16 bit mono
    
    local offset = 0
    
    while offset < datalen do
        local n = math.min(datalen-offset, 1024)
        pda:write(wav:read(n))
        offset += n
    end
    
    wav:close()
    pda:close()
end

local snd = playdate.sound

function playdate.AButtonDown()
    local s = snd.sample.new(2, snd.kFormat16bitMono) -- 2 seconds
    snd.micinput.recordToSample(s,
        function()
            s:save("test.wav")
            wav2pda("test.wav")
        end
    )
end

function playdate.BButtonDown()
    snd.sample.new("test"):play()
end

function playdate.update() end
1 Like

Thinking more about this, it would also be cool if there was some sort of container for user-generated content.

Like .pdx is the container for a bunch of stuff, maybe there could be .pdu for user generated content?

Contents would be images, animations, JSON — anything detailed by the docs of game in question.
That way the pack could be more easily distributed.

With my programmable slideshow thing, it would be:

  • images and animations (preferably uncompiled)
  • sounds and music
  • JSON

Just thinking out loud.

I really like the idea of having a container file for UGC, mods or DLC. It would make it easier to distribute, install and manage extra content for a game. It would be also cleaner, I am not sure that it is safe for the player to dump some files in the save folder for example.

It could work like a pak or wad file basically (or the save folder) and be mounted on top of the pdx file. So if a file exists in both the pdx and container file, the game would use the last one which is usually the container file.

The first thing that come to mind is that the pdx file could protect some folder or file in the meta data so that they would always have the priority over a container for example.

1 Like

That's an interesting point. I had not considered the desire to override files from the pdx.

I wonder how that works right now? It's already possible to put the same filename in both datastore and pdx.

So I tested it! The file in the datastore currently takes precedence. In that the datastore assets are listed after the pdx assets and if they have the same name the original pdx file reference is overwritten in the array/table/list returned by playdate.file.listFiles.

pdx:

  1. music.mp3
  2. sound.pda

datastore:

  1. sound.pda
  2. test.mp3

playdate.file.listFiles():

  1. music.mp3 (from pdx)
  2. sound.pda (from datastore)
  3. test.mp3 (from datastore)

...interesting!

I think this is a problem waiting to happen.

For example I could replace a sprite in a Season 1 game with a half-size equivalent to make it easier and give me the ability to get higher scores (cheat).

2 Likes

I believe this is the expected behavior that is documented in Inside Playdate.

This is very practical because you can have a default save file in the pdx and when you save it it goes in the datastore. It's all transparent.

This is great also for modding potentially so in general I really like the versatility of the system. A container system would be just easier to handle IMO. As I suggested it would be nice to be able to set some file or folders as protected for cases like you mentioned (cheating or general stability).

Absolutely the behaviour is documented and expected.

I just think the the effects/possibilities are not yet fully understood.

Your idea to set certain files/folders as protected is a good one.

A year later, it seems there isn't a way to check if a file was retrieved from the pdx versus the datastore, unless I am missing something? It looks like playdate.file.listFiles() is masking the fact that there's a duplicate name under the hood, so I'm not sure how to detect the original source of the file. Even better, I agree there should be a flag to force file reading to come from the pdx and not the datastore. I'd like to utilize this as a modding feature in some places, but not all, and don't want users potentially overriding built-in levels/assets if they can discern the file path.

2 Likes