[Question] Proper way to create and check for directories

Discussion in '3DS - Homebrew Development and Emulators' started by AxiosAmnesia, Apr 20, 2017.

  1. AxiosAmnesia
    OP

    AxiosAmnesia Newbie

    Newcomer
    8
    3
    Apr 12, 2017
    United States
    I've been writing my own homebrew game recently and I need to write some save files. This itself isn't a problem, but i've been struggling on finding the correct way to handle directory creation and checking for already made directories.

    I've found this snippet of code on the forums that has pointed me in the right direction.
    Code:
    void createDir(const char *path)
    {
        FS_Archive sdArch;
        Result Res = FSUSER_OpenArchive(&sdArch, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""));
        FSUSER_CreateDirectory(sdArch, fsMakePath(PATH_ASCII, path), 0);
    }
    
    Is this a good way of going about creating a directory? How would I go about just checking if a directory exists? What about deleting a directory?

    Thanks
     
    Quantumcat likes this.
  2. erman1337

    erman1337 GBAtemp Maniac

    Member
    1,211
    931
    Sep 27, 2015
    Belgium
    Brussels
    Checking directory:
    Code:
    // this returns 0 on success == false
    if(!FSUSER_OpenDirectory(nullptr, sdArch, fsMakePath(PATH_ASCII, "dir path here")){
    // directory exists
    } else {
    // it doesn't
    }
    Deleting directory:
    Code:
    // fails if the directory isn't empty
    FSUSER_DeleteDirectory(sdArch, fsMakePath(PATH_ASCII, "dir path here"));
    // deletes the contents also
    FSUSER_DeleteDirectoryRecursively(sdArch, fsMakePath(PATH_ASCII, "dir path here"));
     
    Last edited by erman1337, Apr 21, 2017
    B_E_P_I_S_M_A_N likes this.
  3. B_E_P_I_S_M_A_N

    B_E_P_I_S_M_A_N I have graced this thread with my presence.

    Member
    746
    2,726
    Jun 7, 2016
    United States
    Sixth Circle of Hell
    I'm still getting a feel for this, this is what I know so far:

    All files and directories on your SD Card, as well as in the RomFS of your program, are treated as archives. In order to access an archive, you need to create an FS_Archive, and open it with FSUSER_OpenArchive(). Once you're done with it, close with FSUSER_CloseArchive().

    Your method of creating a directory seems fine to me, AFAIK.

    Here's a snippet of code I used in a short program a while back to check for directories in the RomFS.

    Code:
    bool doesDirExist() {
        Handle dirHandle;
        FS_Archive romfsArchive = (FS_Archive) {ARCHIVE_ROMFS, (FS_Path){PATH_EMPTY, 1, (u8*)""}};
        FS_Path path             = fsMakePath(PATH_ASCII, "romfs:/testdir");
    
        FSUSER_OpenArchive(&romfsArchive, ARCHIVE_ROMFS, path);
        Result ret = FSUSER_OpenDirectory(&dirHandle, romfsArchive, path);
    
        if (ret) {
            FSDIR_Close(dirHandle);
            FSUSER_CloseArchive(&romfsArchive);
        }
    
        svcCloseHandle(dirHandle);
    
        if (ret) return false;
        else return true;
    }
    
    Of course, you'd probably want to replace ARCHIVE_ROMFS with ARCHIVE_SDMC, and also add a parameter for the directory path string.
     
    Last edited by B_E_P_I_S_M_A_N, Apr 21, 2017 - Reason: fix the example, whoops
  4. AxiosAmnesia
    OP

    AxiosAmnesia Newbie

    Newcomer
    8
    3
    Apr 12, 2017
    United States
    Thanks for the replies.

    Just went ahead and gave these a test and they both worked for me. Thanks!

    I'm not very familiar with how to use handles, but thank you for your solution.
     
  5. AxiosAmnesia
    OP

    AxiosAmnesia Newbie

    Newcomer
    8
    3
    Apr 12, 2017
    United States
    For anyone that may come across this thread seeking file i/o questions I found a great example in the citra-emu source code.

    https://github.com/citra-emu/hwtests/blob/master/source/tests/fs/fs_sdmc.cpp

    Code:
    
    static bool TestFileWriteRead(FS_Archive sdmcArchive)
    {
    Handle fileHandle;
    u32 bytesWritten;
    u32 bytesRead;
    u64 fileSize;
    
    const static FS_Path filePath = fsMakePath(PATH_ASCII, "/test_file_write_read.txt");
    const static char* stringWritten = "A string\n";
    
    // Create file
    FSUSER_OpenFile(&fileHandle, sdmcArchive, filePath, FS_OPEN_CREATE | FS_OPEN_WRITE, 0);
    SCOPE_EXIT({ // Close and delete file no matter what happens
    FSFILE_Close(fileHandle);
    FSUSER_DeleteFile(sdmcArchive, filePath);
    });
    
    // Write to file
    SoftAssert(FSFILE_Write(fileHandle, &bytesWritten, 0, stringWritten, strlen(stringWritten)+1, FS_WRITE_FLUSH) == 0);
    // Verify string size
    SoftAssert(strlen(stringWritten)+1 == bytesWritten);
    
    // Check file size
    SoftAssert(FSFILE_GetSize(fileHandle, &fileSize) == 0);
    // Verify file size
    SoftAssert(fileSize == bytesWritten);
    
    std::unique_ptr<char> stringRead(new char[fileSize]);
    // Read from file
    SoftAssert(FSFILE_Read(fileHandle, &bytesRead, 0, stringRead.get(), fileSize) == 0);
    // Verify string contents
    SoftAssert(strcmp(stringRead.get(), stringWritten) == 0);
    
    return true;
    }
    
     
    B_E_P_I_S_M_A_N likes this.