rsFileA3D.cpp revision e3150cfb3edb028407669e4a65e087eae77e718c
1
2/*
3 * Copyright (C) 2009 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "rsContext.h"
19#include "rsFileA3D.h"
20
21#include "rsMesh.h"
22#include "rsAnimation.h"
23#include "rs.h"
24
25#if !defined(__RS_PDK__)
26    #include <androidfw/Asset.h>
27#endif
28
29using namespace android;
30using namespace android::renderscript;
31
32FileA3D::FileA3D(Context *rsc) : ObjectBase(rsc) {
33    mAlloc = NULL;
34    mData = NULL;
35    mWriteStream = NULL;
36    mReadStream = NULL;
37    mAsset = NULL;
38
39    mMajorVersion = 0;
40    mMinorVersion = 1;
41    mDataSize = 0;
42}
43
44FileA3D::~FileA3D() {
45    for (size_t i = 0; i < mIndex.size(); i ++) {
46        delete mIndex[i];
47    }
48    for (size_t i = 0; i < mWriteIndex.size(); i ++) {
49        delete mWriteIndex[i];
50    }
51    if (mWriteStream) {
52        delete mWriteStream;
53    }
54    if (mReadStream) {
55        delete mWriteStream;
56    }
57    if (mAlloc) {
58        free(mAlloc);
59    }
60    if (mAsset) {
61#if !defined(__RS_PDK__)
62        delete mAsset;
63#endif
64    }
65}
66
67void FileA3D::parseHeader(IStream *headerStream) {
68    mMajorVersion = headerStream->loadU32();
69    mMinorVersion = headerStream->loadU32();
70    uint32_t flags = headerStream->loadU32();
71    mUse64BitOffsets = (flags & 1) != 0;
72
73    uint32_t numIndexEntries = headerStream->loadU32();
74    for (uint32_t i = 0; i < numIndexEntries; i ++) {
75        A3DIndexEntry *entry = new A3DIndexEntry();
76        headerStream->loadString(&entry->mObjectName);
77        //ALOGV("Header data, entry name = %s", entry->mObjectName.string());
78        entry->mType = (RsA3DClassID)headerStream->loadU32();
79        if (mUse64BitOffsets){
80            entry->mOffset = headerStream->loadOffset();
81            entry->mLength = headerStream->loadOffset();
82        } else {
83            entry->mOffset = headerStream->loadU32();
84            entry->mLength = headerStream->loadU32();
85        }
86        entry->mRsObj = NULL;
87        mIndex.push(entry);
88    }
89}
90
91bool FileA3D::load(Asset *asset) {
92#if !defined(__RS_PDK__)
93    mAsset = asset;
94    return load(asset->getBuffer(false), asset->getLength());
95#else
96    return false;
97#endif
98}
99
100bool FileA3D::load(const void *data, size_t length) {
101    const uint8_t *localData = (const uint8_t *)data;
102
103    size_t lengthRemaining = length;
104    size_t magicStrLen = 12;
105    if ((length < magicStrLen) ||
106        memcmp(data, "Android3D_ff", magicStrLen)) {
107        return false;
108    }
109
110    localData += magicStrLen;
111    lengthRemaining -= magicStrLen;
112
113    // Next we get our header size
114    uint64_t headerSize = 0;
115    if (lengthRemaining < sizeof(headerSize)) {
116        return false;
117    }
118
119    memcpy(&headerSize, localData, sizeof(headerSize));
120    localData += sizeof(headerSize);
121    lengthRemaining -= sizeof(headerSize);
122
123    if (lengthRemaining < headerSize) {
124        return false;
125    }
126
127    // Now open the stream to parse the header
128    IStream headerStream(localData, false);
129    parseHeader(&headerStream);
130
131    localData += headerSize;
132    lengthRemaining -= headerSize;
133
134    if (lengthRemaining < sizeof(mDataSize)) {
135        return false;
136    }
137
138    // Read the size of the data
139    memcpy(&mDataSize, localData, sizeof(mDataSize));
140    localData += sizeof(mDataSize);
141    lengthRemaining -= sizeof(mDataSize);
142
143    if (lengthRemaining < mDataSize) {
144        return false;
145    }
146
147    // We should know enough to read the file in at this point.
148    mData = (uint8_t *)localData;
149    mReadStream = new IStream(mData, mUse64BitOffsets);
150
151    return true;
152}
153
154bool FileA3D::load(FILE *f) {
155    char magicString[12];
156    size_t len;
157
158    ALOGV("file open 1");
159    len = fread(magicString, 1, 12, f);
160    if ((len != 12) ||
161        memcmp(magicString, "Android3D_ff", 12)) {
162        return false;
163    }
164
165    // Next thing is the size of the header
166    uint64_t headerSize = 0;
167    len = fread(&headerSize, 1, sizeof(headerSize), f);
168    if (len != sizeof(headerSize) || headerSize == 0) {
169        return false;
170    }
171
172    uint8_t *headerData = (uint8_t *)malloc(headerSize);
173    if (!headerData) {
174        return false;
175    }
176
177    len = fread(headerData, 1, headerSize, f);
178    if (len != headerSize) {
179        return false;
180    }
181
182    // Now open the stream to parse the header
183    IStream headerStream(headerData, false);
184    parseHeader(&headerStream);
185
186    free(headerData);
187
188    // Next thing is the size of the header
189    len = fread(&mDataSize, 1, sizeof(mDataSize), f);
190    if (len != sizeof(mDataSize) || mDataSize == 0) {
191        return false;
192    }
193
194    ALOGV("file open size = %lli", mDataSize);
195
196    // We should know enough to read the file in at this point.
197    mAlloc = malloc(mDataSize);
198    if (!mAlloc) {
199        return false;
200    }
201    mData = (uint8_t *)mAlloc;
202    len = fread(mAlloc, 1, mDataSize, f);
203    if (len != mDataSize) {
204        return false;
205    }
206
207    mReadStream = new IStream(mData, mUse64BitOffsets);
208
209    ALOGV("Header is read an stream initialized");
210    return true;
211}
212
213size_t FileA3D::getNumIndexEntries() const {
214    return mIndex.size();
215}
216
217const FileA3D::A3DIndexEntry *FileA3D::getIndexEntry(size_t index) const {
218    if (index < mIndex.size()) {
219        return mIndex[index];
220    }
221    return NULL;
222}
223
224ObjectBase *FileA3D::initializeFromEntry(size_t index) {
225    if (index >= mIndex.size()) {
226        return NULL;
227    }
228
229    FileA3D::A3DIndexEntry *entry = mIndex[index];
230    if (!entry) {
231        return NULL;
232    }
233
234    if (entry->mRsObj) {
235        entry->mRsObj->incUserRef();
236        return entry->mRsObj;
237    }
238
239    // Seek to the beginning of object
240    mReadStream->reset(entry->mOffset);
241    switch (entry->mType) {
242        case RS_A3D_CLASS_ID_UNKNOWN:
243            return NULL;
244        case RS_A3D_CLASS_ID_MESH:
245            entry->mRsObj = Mesh::createFromStream(mRSC, mReadStream);
246            break;
247        case RS_A3D_CLASS_ID_TYPE:
248            entry->mRsObj = Type::createFromStream(mRSC, mReadStream);
249            break;
250        case RS_A3D_CLASS_ID_ELEMENT:
251            entry->mRsObj = Element::createFromStream(mRSC, mReadStream);
252            break;
253        case RS_A3D_CLASS_ID_ALLOCATION:
254            entry->mRsObj = Allocation::createFromStream(mRSC, mReadStream);
255            break;
256        case RS_A3D_CLASS_ID_PROGRAM_VERTEX:
257            //entry->mRsObj = ProgramVertex::createFromStream(mRSC, mReadStream);
258            break;
259        case RS_A3D_CLASS_ID_PROGRAM_RASTER:
260            //entry->mRsObj = ProgramRaster::createFromStream(mRSC, mReadStream);
261            break;
262        case RS_A3D_CLASS_ID_PROGRAM_FRAGMENT:
263            //entry->mRsObj = ProgramFragment::createFromStream(mRSC, mReadStream);
264            break;
265        case RS_A3D_CLASS_ID_PROGRAM_STORE:
266            //entry->mRsObj = ProgramStore::createFromStream(mRSC, mReadStream);
267            break;
268        case RS_A3D_CLASS_ID_SAMPLER:
269            //entry->mRsObj = Sampler::createFromStream(mRSC, mReadStream);
270            break;
271        case RS_A3D_CLASS_ID_ANIMATION:
272            //entry->mRsObj = Animation::createFromStream(mRSC, mReadStream);
273            break;
274        case RS_A3D_CLASS_ID_ADAPTER_1D:
275            //entry->mRsObj = Adapter1D::createFromStream(mRSC, mReadStream);
276            break;
277        case RS_A3D_CLASS_ID_ADAPTER_2D:
278            //entry->mRsObj = Adapter2D::createFromStream(mRSC, mReadStream);
279            break;
280        case RS_A3D_CLASS_ID_SCRIPT_C:
281            break;
282    }
283    if (entry->mRsObj) {
284        entry->mRsObj->incUserRef();
285    }
286    return entry->mRsObj;
287}
288
289bool FileA3D::writeFile(const char *filename) {
290    if (!mWriteStream) {
291        ALOGE("No objects to write\n");
292        return false;
293    }
294    if (mWriteStream->getPos() == 0) {
295        ALOGE("No objects to write\n");
296        return false;
297    }
298
299    FILE *writeHandle = fopen(filename, "wb");
300    if (!writeHandle) {
301        ALOGE("Couldn't open the file for writing\n");
302        return false;
303    }
304
305    // Open a new stream to make writing the header easier
306    OStream headerStream(5*1024, false);
307    headerStream.addU32(mMajorVersion);
308    headerStream.addU32(mMinorVersion);
309    uint32_t is64Bit = 0;
310    headerStream.addU32(is64Bit);
311
312    uint32_t writeIndexSize = mWriteIndex.size();
313    headerStream.addU32(writeIndexSize);
314    for (uint32_t i = 0; i < writeIndexSize; i ++) {
315        headerStream.addString(&mWriteIndex[i]->mObjectName);
316        headerStream.addU32((uint32_t)mWriteIndex[i]->mType);
317        if (mUse64BitOffsets){
318            headerStream.addOffset(mWriteIndex[i]->mOffset);
319            headerStream.addOffset(mWriteIndex[i]->mLength);
320        } else {
321            uint32_t offset = (uint32_t)mWriteIndex[i]->mOffset;
322            headerStream.addU32(offset);
323            offset = (uint32_t)mWriteIndex[i]->mLength;
324            headerStream.addU32(offset);
325        }
326    }
327
328    // Write our magic string so we know we are reading the right file
329    String8 magicString(A3D_MAGIC_KEY);
330    fwrite(magicString.string(), sizeof(char), magicString.size(), writeHandle);
331
332    // Store the size of the header to make it easier to parse when we read it
333    uint64_t headerSize = headerStream.getPos();
334    fwrite(&headerSize, sizeof(headerSize), 1, writeHandle);
335
336    // Now write our header
337    fwrite(headerStream.getPtr(), sizeof(uint8_t), headerStream.getPos(), writeHandle);
338
339    // Now write the size of the data part of the file for easier parsing later
340    uint64_t fileDataSize = mWriteStream->getPos();
341    fwrite(&fileDataSize, sizeof(fileDataSize), 1, writeHandle);
342
343    fwrite(mWriteStream->getPtr(), sizeof(uint8_t), mWriteStream->getPos(), writeHandle);
344
345    int status = fclose(writeHandle);
346
347    if (status != 0) {
348        ALOGE("Couldn't close file\n");
349        return false;
350    }
351
352    return true;
353}
354
355void FileA3D::appendToFile(Context *con, ObjectBase *obj) {
356    if (!obj) {
357        return;
358    }
359    if (!mWriteStream) {
360        const uint64_t initialStreamSize = 256*1024;
361        mWriteStream = new OStream(initialStreamSize, false);
362    }
363    A3DIndexEntry *indexEntry = new A3DIndexEntry();
364    indexEntry->mObjectName.setTo(obj->getName());
365    indexEntry->mType = obj->getClassId();
366    indexEntry->mOffset = mWriteStream->getPos();
367    indexEntry->mRsObj = obj;
368    mWriteIndex.push(indexEntry);
369    obj->serialize(con, mWriteStream);
370    indexEntry->mLength = mWriteStream->getPos() - indexEntry->mOffset;
371    mWriteStream->align(4);
372}
373
374RsObjectBase rsaFileA3DGetEntryByIndex(RsContext con, uint32_t index, RsFile file) {
375    FileA3D *fa3d = static_cast<FileA3D *>(file);
376    if (!fa3d) {
377        ALOGE("Can't load entry. No valid file");
378        return NULL;
379    }
380
381    ObjectBase *obj = fa3d->initializeFromEntry(index);
382    //ALOGV("Returning object with name %s", obj->getName());
383
384    return obj;
385}
386
387
388void rsaFileA3DGetNumIndexEntries(RsContext con, int32_t *numEntries, RsFile file) {
389    FileA3D *fa3d = static_cast<FileA3D *>(file);
390
391    if (fa3d) {
392        *numEntries = fa3d->getNumIndexEntries();
393    } else {
394        *numEntries = 0;
395    }
396}
397
398void rsaFileA3DGetIndexEntries(RsContext con, RsFileIndexEntry *fileEntries, uint32_t numEntries, RsFile file) {
399    FileA3D *fa3d = static_cast<FileA3D *>(file);
400
401    if (!fa3d) {
402        ALOGE("Can't load index entries. No valid file");
403        return;
404    }
405
406    uint32_t numFileEntries = fa3d->getNumIndexEntries();
407    if (numFileEntries != numEntries || numEntries == 0 || fileEntries == NULL) {
408        ALOGE("Can't load index entries. Invalid number requested");
409        return;
410    }
411
412    for (uint32_t i = 0; i < numFileEntries; i ++) {
413        const FileA3D::A3DIndexEntry *entry = fa3d->getIndexEntry(i);
414        fileEntries[i].classID = entry->getType();
415        fileEntries[i].objectName = entry->getObjectName().string();
416    }
417}
418
419RsFile rsaFileA3DCreateFromMemory(RsContext con, const void *data, uint32_t len) {
420    if (data == NULL) {
421        ALOGE("File load failed. Asset stream is NULL");
422        return NULL;
423    }
424
425    Context *rsc = static_cast<Context *>(con);
426    FileA3D *fa3d = new FileA3D(rsc);
427    fa3d->incUserRef();
428
429    fa3d->load(data, len);
430    return fa3d;
431}
432
433RsFile rsaFileA3DCreateFromAsset(RsContext con, void *_asset) {
434#if !defined(__RS_PDK__)
435    Context *rsc = static_cast<Context *>(con);
436    Asset *asset = static_cast<Asset *>(_asset);
437    FileA3D *fa3d = new FileA3D(rsc);
438    fa3d->incUserRef();
439
440    fa3d->load(asset);
441    return fa3d;
442#else
443    return NULL;
444#endif
445}
446
447RsFile rsaFileA3DCreateFromFile(RsContext con, const char *path) {
448    if (path == NULL) {
449        ALOGE("File load failed. Path is NULL");
450        return NULL;
451    }
452
453    Context *rsc = static_cast<Context *>(con);
454    FileA3D *fa3d = NULL;
455
456    FILE *f = fopen(path, "rb");
457    if (f) {
458        fa3d = new FileA3D(rsc);
459        fa3d->incUserRef();
460        fa3d->load(f);
461        fclose(f);
462    } else {
463        ALOGE("Could not open file %s", path);
464    }
465
466    return fa3d;
467}
468