How do I save a file?

I am building a game. I would like to be able to save and load the game. I am writing everything I need to save (variables, instructions to draw screen, script + line) to a database. However, I would very much like to add a screenshot. I don’t know how to put that into a database, and also, because of the large size, I prefer it to be a file, and save only the file location to the database.
But I also do not know how to save a file.
I use gameRoot.grabToImage(function(result)) to collect a screenshot. Then result.saveToFile(filename).
What is the file location? The name is “save_” + slot + “.jpg”. I guess I will need to reference a path. What location can I write to without breaking out of the sandbox?
Below is a code snippet for reference. You can find the screenshot code in the commitSave() function.
This code is human written, with modifications done by a human after AI review, except for 1 thing: the grabToImage->saveToFile routine was suggested by AI and mostly unmodified.

/////////////////////////
// --- Save game logic

function stageSaveGame() {
    Components.SaveManager.stagePendingSave(
                Engine.currentScene,
                Engine.cmdIndex,
                Engine.variables,
                buildScreenBlob(),
                "placeholder") //TODO: return chapter label, to be implemented
}

function commitSave(slot) {
    if ((slot === null )) return false
    if ((slot < 0 )) return false
    gameRoot.grabToImage(function(result) {
        var filename = "save_" + slot + ".jpg"   ///Is this correct? How are files saved? Do I need to reference full path?
        result.saveToFile(filename)
        Components.SaveManager.commitSave(filename)
        _saving = false
    }, Qt.size(thumbWidth, thumbHeight))
}

// Quick save menu item:
function doQuickSave() {
    stageSaveGame()
    Components.SaveManager.finalizePending(0, "Quicksave")
    commitSave(0)
}

// Save menu item:
function doManualSave() {
    stageSaveGame()
    pageStack.push(Qt.resolvedUrl("Save.qml"))
}

function computeThumbSize() {
    // For thumbscreen in saving and loading
    thumbWidth  = Math.round(Screen.width * Components.Constants.thumbScreenFraction)
    thumbHeight = Math.round(thumbWidth * gameRoot.height / gameRoot.width)
}

function buildScreenBlob() {
    var sprites = []
    for (var i = 0; i < spriteModel.count; i++) {
        var s = spriteModel.get(i)
        sprites.push({
            spriteId:     s.spriteId,
            spriteSource: s.spriteSource,
            position:     s.position
        })
    }
    // TODO: SOUND
    return {
        background: currentBg,
        sprites:    sprites
    }
}

function buildScreenFromScreenBlob(screenBlob) {
    currentBg = screenBlob.background
    for (var i = 0; i < screenBlob.sprites.length; i++) {
        var s = screenBlob.sprites[i]
        showSprite(s.spriteId, s.spriteSource, s.position)
    }
    //TODO: sound
}

onStatusChanged: {
    if (status === PageStatus.Active && !_saving && Components.SaveManager.pendingSlot() >= 0) {
        _saving = true
        commitSave(Components.SaveManager.pendingSlot())
    }
    if (status === PageStatus.Active && Components.SaveManager.justLoaded()) {
        processNext()
    }
}

// --- End save game logic
/////////////////////////

EDIT:
To be more clear. The goal of the screenshot is as a visual aid for differentiating save games. I don’t need to access the screenshots from anywhere but the app itself.

I’d imagine it’s wherever you are launching the app from

1 Like

Just a side note: If it’s a screenshot, I would save it as a png file, not as jpg.

2 Likes

Why?
My reasoning for jpg: I need no transparency, and jpg has better compression (I think).
What are the reasons for png?

How sure? “I’d imagine” does sound like little more than a guess to me, but it might be a matter of casual speech.
I’d guess that one too, but what about the current working directory? Will the app give missing file errors if I switch to a terminal and cd into another directory?
What about qrc:// style links? I found this: Qt.resolvedUrl(): Best Practices and Alternatives but I am not experienced enough to fully understand.

use the xdg support that’s built in. In qml:

property string imagePath: StandardPaths.PicturesLocation

also known as StandardPaths.pictures. I use this latter style mostly, which also works for documents, for instance.

AKA StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]

I have a number of methods in a number of backends to save files. From the camera, for instance:

    var filename = imagePath +"/" + seriesName 
    camera.imageCapture.captureToLocation(filename)

EDIT, don’t forget that the app needs the permissions to write in sailjail.

1 Like

Do you know of any way to write to a location inside the sandbox? If this is the only way, I will do so, but I’d prefer keeping the sandbox as closed as possible.

I probably wasn’t too clear on my goals.
The screenshot is for visually differentiating save games only. So I don’t need to reach the file from anywhere, but from the app itself.

Sure. Just use something like:

StandardPaths.writableLocation(StandardPaths.HomeLocation) + '.local/share/myorg/myapp' 

You could use + ‘.cache/myorg/myapp’ or ‘.config/myorg/myapp’ as well.

EDIT: from python I sometimes do things like:

  def get_home_path ():
      homeDir = str(Path.home())
      pyotherside.send('homePathFolder', homeDir )

But that’s actually silly.

2 Likes

Thank you! I’ll try writing to .local/share/myorg/myapp/

You do need a complete path, so use StandardPaths.home as a prefix. ‘~/.’ will not work :slight_smile:

2 Likes

Sorry, I think that is what you get with:

StandardPaths.writableLocation(StandardPaths.AppLocalDataLocation);

EDIT, sorry: QStandardPaths Class | Qt Core | Qt 6.11.1

1 Like

We need Qt5 right? Not Qt6?

But it is from Qt.labs.platform
I don’t see it in the API checklist for harbour compliance. Is it allowed to use? Or is there another API that provides StandardPaths?

That has not changed. That’s just an old link. EDIT you don’t need any extra includes for that. i have
import QtQuick 2.0
import Sailfish.Silica 1.0

and that’s it in one case.

1 Like

Huh, I couldn’t and still can’t find it anywhere in the documentation. But I see it is possible from other source code, like sailjail_test/qml/sailjailtest.qml at master · nephros/sailjail_test · GitHub

How could I have known I could use this? I clearly need some pointers in finding the correct documentation. Is it in Sailfish.Silica? I can only find qml object documentation for that.

The source you quote from @nephros is right out of the docs: as I said,

also known as StandardPaths.pictures.
is a short form. and StandardPaths.home is short for
StandardPaths.writableLocation(StandardPaths.HomeLocation)

EDIT. In the IDE, if editing a QML file, there is completion and context help (f1):

The way I look it up in the main documentation:
First find a path if it’s not in my head (rarely) get them from the c++ docs :

Then if I forget the ‘standard’ syntax for qml:

2 Likes

You have my thanks, Mark. You not only helped me with this blocker, but most likely streamlined my workflow a bit as well.
I will try to finish this code snippet this evening, and will post my likely success.

1 Like

Ah, I’ve been stumbling about the docs and other peoples’ code for so long I’m starting to be an upstart :wink:

AFAIK jpg has lossy compression and png has lossless. But of course it depends on the purpose what’s more appropriate.

1 Like

Great success. I found a tiny image in the correct folder, without permissions and breaking the sandbox.
It took me the whole evening and part of the night though.
My first test did give me a white screen with no errors. Had to search for almost 3 hours to find circular imports of singletons were the problem. Well, next time I’ll catch it more quickly!
I did not test loading yet, but I can’t believe that it wouldn’t work if I further implemented it (apart from fixing some mistakes first of course).

Thanks again.

1 Like