Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use ifstream in Android NDK to access Assets?

My question is pretty simple, but I am having a hard time finding any info about this online.

Is it possible to use ifstream to open a file from assets and/or resources using Android NDK?

For example, placing a test.txt file in /assets and trying the following does not work:

    char pLine[256];
    std::ifstream fin("/assets/test.txt");
    if(!fin.fail())
    {
        LOGD( "test.txt opened" );
        while( !fin.eof() )
        {
            fin.getline( pLine, 256 );
            LOGD(pLine);
        }
    }
    else
    {
        LOGD( "test.txt FAILED TO OPEN!" );
    }
    fin.close();

Nor does any variable of:

    std::ifstream fin("assets/test.txt");

    std::ifstream fin("test.txt");

Etc..., nor placing it in /res instead.

So, is it possible to use normal ifstream operators to access assets and or resource files?

like image 371
Nitzan Wilnai Avatar asked Jun 16 '12 13:06

Nitzan Wilnai


2 Answers

It is right that std::ifstream cannot be used, but one can create an assetistream that could be used in a similar way. For example:

class asset_streambuf: public std::streambuf
{
public:
    asset_streambuf(AAssetManager* manager, const std::string& filename)
    : manager(manager)
    {
        asset = AAssetManager_open(manager, filename.c_str(), AASSET_MODE_STREAMING);
        buffer.resize(1024);

        setg(0, 0, 0);
        setp(&buffer.front(), &buffer.front() + buffer.size());
    }

    virtual ~asset_streambuf()
    {
        sync();
        AAsset_close(asset);
    }

    std::streambuf::int_type underflow() override
    {
        auto bufferPtr = &buffer.front();
        auto counter = AAsset_read(asset, bufferPtr, buffer.size());

        if(counter == 0)
            return traits_type::eof();
        if(counter < 0) //error, what to do now?
            return traits_type::eof();

        setg(bufferPtr, bufferPtr, bufferPtr + counter);

        return traits_type::to_int_type(*gptr());
    }

    std::streambuf::int_type overflow(std::streambuf::int_type value) override
    {
        return traits_type::eof();
    };

    int sync() override
    {
        std::streambuf::int_type result = overflow(traits_type::eof());

        return traits_type::eq_int_type(result, traits_type::eof()) ? -1 : 0;
    }

private:
    AAssetManager* manager;
    AAsset* asset;
    std::vector<char> buffer;
};


class assetistream: public std::istream
{
public:
    assetistream(AAssetManager* manager, const std::string& file)
    : std::istream(new asset_streambuf(manager, file))
    {
    }
    assetistream(const std::string& file)
    : std::istream(new asset_streambuf(manager, file))
    {
    }

    virtual ~assetistream()
    {
        delete rdbuf();
    }

    static void setAssetManager(AAssetManager* m)
    {
        manager = m;
    }

private:
    static AAssetManager* manager;
};

void foo(AAssetManager* manager)
{
    assetistream::setAssetManager(manager);

    assetistream as("text/tmp.txt");
    std::string s;

    std::getline(as, s);
}

Improvements are very welcome.

like image 95
Tobias Wollgam Avatar answered Sep 18 '22 05:09

Tobias Wollgam


No, you cannot. Assets are stored within the apk, a zip file. ifstream cannot read within the zip file.

To access these files you either need to access them in java and save them elsewhere or extract the contents of the apk to get to the assets.

Here is an example of doing the former.

http://www.itwizard.ro/android-phone-installing-assets-how-to-60.html

Here is an example of doing the latter.

http://www.anddev.org/ndk_opengl_-_loading_resources_and_assets_from_native_code-t11978.html

like image 29
Jug6ernaut Avatar answered Sep 18 '22 05:09

Jug6ernaut