XGameSaveReadBlobDataAsync

Read XGameSaveBlob data from a XGameSaveContainer asynchronously.

Syntax

HRESULT XGameSaveReadBlobDataAsync(  
         XGameSaveContainerHandle container,  
         const char** blobNames,  
         uint32_t countOfBlobs,  
         XAsyncBlock* async  
)  

Parameters

container   _In_
Type: XGameSaveContainerHandle

Container holding the gamesave blob data..

blobNames   _In_opt_z_count_(countOfBlobs)
Type: char**

Pointer to an array of strings representing the XGameSaveBlob names.

countOfBlobs   _In_
Type: uint32_t

Number of blobs to read.

async   _In_
Type: XAsyncBlock*

AsyncBlock containing the context and callback function for the XGameSaveReadBlobDataAsync call.

Return value

Type: HRESULT

Function result.

Remarks

Results and data are captured in the XGameSaveReadBlobDataResult function. XGameSaveReadBlobDataResult will return the count of blobs in the container as well as the blob data itself. There is a synchronous version of this function called XGameSaveReadBlobData.

// ASYNC Read - can be kicked off from a time sensitive thread 
//              actual work and completion will be scheduled based upon  
//              the configuration of the async_queue tied to the XAsyncBlock 
void Sample::_ReadContainerBlobsAsync(const XGameSaveContainerInfo* container) 
{ 
    const char* blobNames[] = { 
        "WorldState", 
        "PlayerState", 
        "PlayerInventory" 
    }; 
  
    struct LoadContext 
    { 
        LoadContext(Sample* s) : container(nullptr), self(s) {} 
        ~LoadContext() 
        { 
            XGameSaveCloseContainer(container); 
        } 
        XAsyncBlock async; 
        XGameSaveContainerHandle container; 
        Sample* self; 
    }; 
    HRESULT hr; 
    LoadContext* loadContext = new LoadContext(this); 
    if (loadContext == nullptr) 
    { 
        hr = E_OUTOFMEMORY; 
    } 
  
    auto completionCallback = [](XAsyncBlock* async) 
    { 
        auto ctx = reinterpret_cast<LoadContext*>(async->context); 
        auto self = ctx->self; 
        size_t allocatedSize; 
        XGameSaveBlob* blobData = nullptr; 
        uint32_t blobCount = 0; 
  
        HRESULT hr = GetAsyncStatus(async, false); 
  
        if (SUCCEEDED(hr)) 
        { 
            // use local wrapper for GetAsyncResultSize to give strongly typed alloc 
            XGameSaveBlob* blobData = _AllocAsyncResult<XGameSaveBlob>(async, &allocatedSize); 
            if (blobData == nullptr) 
            { 
                hr = E_OUTOFMEMORY; 
            } 
        } 
  
        if (SUCCEEDED(hr)) 
        { 
            // now that we have allocated the required buffers 
            // ask XGameSave to populate them 
            hr = XGameSaveReadBlobDataResult(async, allocatedSize, blobData, &blobCount); 
        } 
        if (SUCCEEDED(hr)) 
        { 
            if (blobCount == _countof(blobNames)) 
            { 
                for (uint32_t i = 0; i < blobCount; i++) 
                { 
                    XGameSaveBlob* currentBlob = blobData + i; 
                    if (strcmp(currentBlob->info.name, "WorldState") == 0) 
                    { 
                        hr = self->_LoadSaveBlob(currentBlob, self->_worldState); 
                    } 
                    else if (strcmp(currentBlob->info.name, "PlayerState") == 0) 
                    { 
                        hr = self->_LoadSaveBlob(currentBlob, self->_playerState); 
                    } 
                    else if (strcmp(currentBlob->info.name, "PlayerInventory") == 0) 
                    { 
                        hr = self->_LoadSaveBlob(currentBlob, self->_playerInventory); 
                    } 
                    if (FAILED(hr)) 
                    { 
                        break; 
                    } 
                } 
            } 
            else 
            { 
                // what containers are missing? Can we get by without XXX? 
                hr = E_UNEXPECTED; 
            } 
        } 
  
        self->_HandleContainerBlobErrors(hr); 
  
        if (blobData != nullptr) 
        { 
            // we own the buffer so better kill it 
            free(blobData); 
        } 
        // be sure to clear this since it will be no longer valid after we exit 
        self->_asyncLoad = nullptr; 
        // kill the temp context tracking the async op 
        delete ctx; 
    }; 
  
    if (SUCCEEDED(hr)) 
    { 
        loadContext->async.context = loadContext; 
        // set the XTaskQueueHandle so the completion will be done on our thread 
        // and then we can allocate the larger buffers with less lock contention 
        loadContext->async.queue = _asyncCompleteQueue; 
        loadContext->async.callback = completionCallback; 
    } 
  
    hr = XGameSaveCreateContainer(_provider, container->name, &loadContext->container); 
    if (SUCCEEDED(hr)) 
    { 
        hr = XGameSaveReadBlobDataAsync(loadContext->container, blobNames, _countof(blobNames), &loadContext->async); 
    } 
    if (SUCCEEDED(hr)) 
    { 
        // keep a reference to this so we can Cancel later if needed 
        _asyncLoad = &loadContext->async;  
        // hand over ownership to the async callback 
        loadContext = nullptr;  
    } 
  
    if (loadContext) 
    { 
        delete loadContext; 
    } 
    if (FAILED(hr)) 
    { 
        _HandleContainerBlobErrors(hr); 
    } 
} 
 
// Allocate a strongly typed portion from an Async Result 
template<typename T> 
static T* _AllocAsyncResult(XAsyncBlock* async, size_t* allocatedSize) 
{ 
    *allocatedSize = 0; 
    size_t allocSize = 0; 
    HRESULT hr = XAsyncGetResultSize(async, &allocSize); 
    if (SUCCEEDED(hr) && allocSize > 0) 
    { 
        *allocatedSize = allocSize; 
        return reinterpret_cast<T*>(malloc(allocSize)); 
    } 
    return nullptr; 
} 
  
/*static*/ 
DWORD Sample::_CompleteThreadProc(PVOID context) 
{ 
    auto self = reinterpret_cast<Sample*>(context); 
  
    while (DWORD wait = WaitForSingleObjectEx(self->_shutdownEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION) 
    { 
        // loop waiting for APC, any other return should exit the thread 
    } 
} 
  
// Init thread and async queue 
HRESULT Sample::_InitQueueThread() 
{ 
    HRESULT hr = S_OK; 
    _shutdownEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); 
    if (_shutdownEvent == nullptr) 
    { 
        hr = HRESULT_FROM_WIN32(GetLastError()); 
    } 
     
    if (SUCCEEDED(hr)) 
    { 
        _completeThread = CreateThread(nullptr, 0, _CompleteThreadProc, this, 0, &_completeThreadId); 
        if (_completeThread == nullptr) 
        { 
            hr = HRESULT_FROM_WIN32(GetLastError()); 
        } 
    } 
    if (SUCCEEDED(hr)) 
    { 
        hr = XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::Manual, &_asyncCompleteQueue); 
    } 
    return hr; 
} 
  
void Sample::_CancelReadContainerBlobsAsync() 
{ 
    if (_asyncLoad) 
    { 
        XAsyncCancel(_asyncLoad); 
    } 
}

Requirements

Header: XGameSave.h

Library: xgameruntime.lib

Supported platforms: Windows, Xbox One family consoles and Xbox Series consoles

See also

XGameSave
XGameSaveBlobInfo
XGameSaveReadBlobDataResult
XGameSaveReadBlobData
Game save errors