Initial revision
[blender.git] / source / blender / readblenfile / intern / BLO_readblenfile.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  *
32  */
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38
39 #ifdef WIN32
40 #include <io.h>         // read, open
41 #else // ! WIN32
42 #include <unistd.h>             // read
43 #endif
44
45 #include "BLO_readStreamGlue.h"
46
47 #include "BLO_readfile.h"
48 #include "BLO_readblenfile.h"
49
50 #include "BKE_blender.h"
51
52 #define CACHESIZE 100000
53
54 char *headerMagic = "BLENDFI";
55
56 void BLO_setversionnumber(char array[4], int version)
57 {
58         memset(array, 0, sizeof(array));
59
60         array[1] = version / 100;
61         array[2] = version % 100;
62 }
63
64 void BLO_setcurrentversionnumber(char array[4])
65 {
66         BLO_setversionnumber(array, BLENDER_VERSION);
67 }
68
69 #ifndef O_BINARY
70 #define O_BINARY 0
71 #endif
72
73 struct BLO_readblenfileStruct {
74         struct readStreamGlueStruct *streamGlue;
75         int fileDes;
76         unsigned int cacheSize;
77         unsigned int inCache;
78         unsigned int leftToRead;
79         unsigned int Seek;
80
81         int (*read)(struct BLO_readblenfileStruct *readblenfileStruct, void *buffer, int size);
82
83         char *readCache;
84         char *fromBuffer;
85         int  fromBufferSize;
86         char crInBuffer;
87         char removeCR;
88 };
89
90 // declare static functions
91
92 static int readfromfilehandle(
93         struct BLO_readblenfileStruct *readblenfileStruct,
94         void *buffer,
95         int size);
96
97 static int readfrommemory(
98         struct BLO_readblenfileStruct *readblenfileStruct,
99         void *buffer,
100         int size);
101
102 static int fillcache(
103         struct BLO_readblenfileStruct *readblenfileStruct);
104
105 static unsigned int readfromcache(
106         struct BLO_readblenfileStruct *readblenfileStruct,
107         void * buffer,
108         unsigned int size);
109
110 static BlendFileData *readblenfilegeneric(
111         struct BLO_readblenfileStruct *readblenfileStruct, 
112         BlendReadError *error_r);
113
114 // implementation of static functions
115
116 static int readfromfilehandle(
117         struct BLO_readblenfileStruct *readblenfileStruct,
118         void *buffer,
119         int size)
120 {
121         int readsize = -1;
122
123         if (readblenfileStruct->fileDes != -1) {
124                 readsize = read(readblenfileStruct->fileDes, buffer, size);
125         }
126
127         return(readsize);
128 }
129
130 static int readfrommemory(
131         struct BLO_readblenfileStruct *readblenfileStruct,
132         void *buffer,
133         int size)
134 {
135         int readsize = -1;
136
137         if (readblenfileStruct->fromBuffer) {
138                 if (size > readblenfileStruct->fromBufferSize) {
139                         size = readblenfileStruct->fromBufferSize;
140                 }
141
142                 memcpy(buffer, readblenfileStruct->fromBuffer, size);
143                 readblenfileStruct->fromBufferSize -= size;
144                 readblenfileStruct->fromBuffer += size;
145
146                 readsize = size;
147         }
148
149         return(readsize);
150 }
151
152 static int fillcache(
153         struct BLO_readblenfileStruct *readblenfileStruct)
154 {
155         int readsize;
156         int toread;
157
158         // how many bytes can we read ?
159
160         toread = readblenfileStruct->leftToRead;
161
162         if (toread > readblenfileStruct->cacheSize) {
163                 toread = readblenfileStruct->cacheSize;
164         }
165
166         readsize = readblenfileStruct->read(readblenfileStruct, readblenfileStruct->readCache, toread);
167         if (readsize > 0) {
168                 if (readblenfileStruct->removeCR) {
169                         // do some stuff here
170                 }
171                 readblenfileStruct->inCache = readsize;
172                 readblenfileStruct->leftToRead -= readsize;
173         }
174
175         return (readsize);
176 }
177
178
179 static unsigned int readfromcache(
180         struct BLO_readblenfileStruct *readblenfileStruct,
181         void * buffer,
182         unsigned int size)
183 {
184         unsigned int readsize = 0;
185
186         if (readblenfileStruct->inCache - readblenfileStruct->Seek > size) {
187                 memcpy(buffer, readblenfileStruct->readCache + readblenfileStruct->Seek, size);
188                 readblenfileStruct->Seek += size;
189                 readsize = size;
190         } else {
191                 // handle me
192         }
193
194         return(readsize);
195 }
196
197 static BlendReadError brs_to_bre(int err)
198 {
199         int errFunction = BRS_GETFUNCTION(err);
200         int errGeneric =  BRS_GETGENERR(err);
201         int errSpecific = BRS_GETSPECERR(err);
202
203         if (errGeneric) {
204                 switch (errGeneric) {
205                 case BRS_MALLOC:
206                         return BRE_OUT_OF_MEMORY;
207                 case BRS_NULL:
208                         return BRE_INTERNAL_ERROR;
209                 case BRS_MAGIC:
210                         return BRE_NOT_A_BLEND;
211                 case BRS_CRCHEADER:
212                 case BRS_CRCDATA:
213                         return BRE_CORRUPT;
214                 case BRS_DATALEN:
215                         return BRE_INCOMPLETE;
216                 case BRS_STUB:
217                         return BRE_NOT_A_BLEND;
218                 }
219         } else if (errSpecific) {
220                 switch (errFunction) {
221                 case BRS_READSTREAMGLUE:
222                         switch (errSpecific) {
223                         case BRS_UNKNOWN:
224                                 return BRE_INTERNAL_ERROR;
225                         }
226                         break;
227                 case BRS_READSTREAMFILE:
228                         switch (errSpecific) {
229                         case BRS_NOTABLEND:
230                                 return BRE_NOT_A_BLEND;
231                         case BRS_READERROR:
232                                 return BRE_UNABLE_TO_READ;
233                         }
234                         break;
235                 case BRS_INFLATE:
236                         switch (errSpecific) {
237                         case BRS_INFLATEERROR:
238                                 return BRE_CORRUPT;
239                         }
240                         break;
241                 case BRS_DECRYPT:
242                         switch (errSpecific) {
243                         case BRS_RSANEWERROR:
244                                 return BRE_INTERNAL_ERROR;
245                         case BRS_DECRYPTERROR:
246                                 return BRE_INTERNAL_ERROR;
247                         case BRS_NOTOURPUBKEY:
248                                 return BRE_NOT_ALLOWED;
249                         }
250                         break;
251                 case BRS_VERIFY:
252                         switch (errSpecific) {
253                         case BRS_RSANEWERROR:
254                                 return BRE_INTERNAL_ERROR;
255                         case BRS_SIGFAILED:
256                                 return BRE_INTERNAL_ERROR;
257                         }
258                         break;
259                 }
260         }
261         
262         return BRE_INVALID;
263 }
264
265 static BlendFileData *readblenfilegeneric(
266         struct BLO_readblenfileStruct *readblenfileStruct, 
267         BlendReadError *error_r)
268 {
269         BlendFileData *bfd= NULL;
270         unsigned char reserved[BLO_RESERVEDSIZE];
271         uint8_t minversion[4];
272         uint8_t myversion[4];
273         uint8_t version[4];
274         uint8_t flags[4];
275         void *parms[2];
276         int filesize;
277         
278         parms[0]= &bfd;
279         parms[1]= error_r;
280
281         BLO_setcurrentversionnumber(myversion);
282
283         readblenfileStruct->cacheSize      = CACHESIZE;
284         readblenfileStruct->readCache      = malloc(readblenfileStruct->cacheSize);
285
286         if (fillcache(readblenfileStruct) <= 0) {
287                 *error_r = BRE_UNABLE_TO_READ;
288         } else if (readfromcache(readblenfileStruct, minversion, sizeof(minversion)) != sizeof(minversion)) {
289                 *error_r = BRE_UNABLE_TO_READ;
290         } else if (memcmp(minversion, myversion, sizeof(minversion)) > 0) {
291                 *error_r = BRE_TOO_NEW;
292         } else if (readfromcache(readblenfileStruct, version,  sizeof(version)) != sizeof(version)) {
293                 *error_r = BRE_UNABLE_TO_READ;
294         } else if (readfromcache(readblenfileStruct, flags,    sizeof(flags)) != sizeof(flags)) {
295                 *error_r = BRE_UNABLE_TO_READ;
296         } else if (readfromcache(readblenfileStruct, &filesize, sizeof(filesize)) != sizeof(filesize)) {
297                 *error_r = BRE_UNABLE_TO_READ;
298         } else if (readfromcache(readblenfileStruct, reserved, sizeof(reserved)) != sizeof(reserved)) {
299                 *error_r = BRE_UNABLE_TO_READ;
300         }  else {
301                 filesize = ntohl(filesize);
302
303                 // substract number of bytes we've
304                 // been handling outside readfromcache()
305                 filesize -= strlen(headerMagic);
306                 filesize--;
307
308                 if (filesize < readblenfileStruct->inCache) {
309                         // we've allready read more than we're supposed to
310                         readblenfileStruct->inCache   = filesize;
311                         readblenfileStruct->leftToRead = 0;                                                                     
312                 } else {
313                         // 
314                         readblenfileStruct->leftToRead = filesize - readblenfileStruct->inCache;
315                 }
316
317                 do {
318                         int err;
319
320                         *error_r = BRE_NONE;
321                         err = readStreamGlue(
322                                 parms,
323                                 &(readblenfileStruct->streamGlue),
324                                 readblenfileStruct->readCache + readblenfileStruct->Seek,
325                                 readblenfileStruct->inCache - readblenfileStruct->Seek);
326
327                         readblenfileStruct->inCache = 0;
328                         readblenfileStruct->Seek = 0;
329
330                         if (err) {
331                                 bfd = NULL;
332
333                                         /* If *error_r != BRE_NONE then it is
334                                          * blo_readstreamfile_end signaling an error
335                                          * in the loading code. Otherwise it is some
336                                          * other part of the streamglue system signalling
337                                          * and error so we convert the BRS error into
338                                          * a BRE error.
339                                          * 
340                                          * Does this have to be so convoluted? No.
341                                          */
342                                 if (*error_r == BRE_NONE) {
343                                         *error_r = brs_to_bre(err);
344                                 }
345                                 
346                                 break;
347                         }
348                 } while (fillcache(readblenfileStruct) > 0);
349         }
350
351         free(readblenfileStruct->readCache);
352         readblenfileStruct->readCache = 0;
353
354         return bfd;
355 }
356
357 // implementation of exported functions
358
359 BlendFileData *
360 BLO_readblenfilememory(
361         char *fromBuffer, 
362         int fromBufferSize, 
363         BlendReadError *error_r)
364 {
365         static char *functionality_check= "\0FUNCTIONALITY_CHECK += BLO_readblenfilememory\n";
366         int magiclen = strlen(headerMagic);
367         BlendFileData *bfd = NULL;
368
369         if (!fromBuffer) {
370                 *error_r = BRE_UNABLE_TO_OPEN;
371         } else if (fromBufferSize < magiclen) {
372                 *error_r = BRE_UNABLE_TO_READ;
373         } else if (strncmp(fromBuffer, headerMagic, magiclen) != 0) {
374                 *error_r = BRE_NOT_A_BLEND;
375         } else if (fromBufferSize < magiclen+1) {
376                 *error_r = BRE_UNABLE_TO_READ;
377         } else if (fromBuffer[magiclen] != '\r' && fromBuffer[magiclen] != '\n') {
378                 *error_r = BRE_NOT_A_BLEND;
379         } else {
380                 int crnl; 
381
382                 fromBuffer+= magiclen;
383                 fromBufferSize-= magiclen;
384                 crnl = (fromBuffer[0] == '\r');
385                 fromBuffer++;
386                 fromBufferSize--;
387                 
388                 if (crnl && fromBufferSize<1) {
389                         *error_r = BRE_UNABLE_TO_READ;
390                 } else {
391                         struct BLO_readblenfileStruct *readblenfileStruct = NULL;
392
393                                 /* skip carriage return if necessary */
394                         if (crnl) {
395                                 fromBuffer++;
396                                 fromBufferSize--;
397                         }
398
399                         // Allocate all the stuff we need
400                         readblenfileStruct = calloc(sizeof(struct BLO_readblenfileStruct), 1);
401                         readblenfileStruct->fileDes        = -1;
402                         readblenfileStruct->fromBuffer     = fromBuffer;
403                         readblenfileStruct->fromBufferSize = fromBufferSize;
404                         readblenfileStruct->read                   = readfrommemory;
405
406                         readblenfileStruct->removeCR   = crnl;
407                         // fake filesize for now until we've
408                         // actually read in the filesize from the header
409                         // make sure we don't read more bytes than there
410                         // are left to handle accoding to fromBufferSize
411                         readblenfileStruct->leftToRead = readblenfileStruct->fromBufferSize;
412
413                         bfd = readblenfilegeneric(readblenfileStruct, error_r);
414
415                         free(readblenfileStruct);
416                         readblenfileStruct = 0;
417                 }
418         }
419
420         return bfd;
421 }
422
423
424 BlendFileData *
425 BLO_readblenfilehandle(
426         int fd, 
427         BlendReadError *error_r)
428 {
429         static char *functionality_check= "\0FUNCTIONALITY_CHECK += BLO_readblenfilehandle\n";
430         int magiclen = strlen(headerMagic);
431         BlendFileData *bfd = NULL;
432         char tempbuffer[256];
433         
434         if (fd==-1) {
435                 *error_r = BRE_UNABLE_TO_OPEN;
436         } else if (read(fd, tempbuffer, magiclen) != magiclen) {
437                 *error_r = BRE_UNABLE_TO_READ;
438         } else if (strncmp(tempbuffer, headerMagic, magiclen) != 0 ) {
439                 *error_r = BRE_NOT_A_BLEND;
440         } else if (read(fd, tempbuffer, 1) != 1) {
441                 *error_r = BRE_UNABLE_TO_READ;
442         } else if (tempbuffer[0] != '\r' && tempbuffer[0] != '\n') {
443                 *error_r = BRE_NOT_A_BLEND;
444         } else {
445                 int crnl = (tempbuffer[0] == '\r');
446                 
447                 if (crnl && read(fd, tempbuffer, 1)!=1) {
448                         *error_r = BRE_UNABLE_TO_READ;
449                 } else {
450                         struct BLO_readblenfileStruct *readblenfileStruct;
451
452                         // Allocate all the stuff we need
453                         readblenfileStruct = calloc(sizeof(struct BLO_readblenfileStruct), 1);
454                         readblenfileStruct->fileDes    = fd;
455                         readblenfileStruct->read       = readfromfilehandle;
456
457                         readblenfileStruct->removeCR   = crnl;
458                         // fake filesize for now until we've
459                         // actually read in the filesize from the header
460                         readblenfileStruct->leftToRead = CACHESIZE;
461
462                         bfd = readblenfilegeneric(readblenfileStruct, error_r);
463
464                         free(readblenfileStruct);
465                         readblenfileStruct = 0;
466                 }
467         }
468
469         return bfd;
470 }
471
472 BlendFileData *
473 BLO_readblenfilename(
474         char *fileName, 
475         BlendReadError *error_r)
476 {
477         static char *functionality_check= "\0FUNCTIONALITY_CHECK += BLO_readblenfilename\n";
478         BlendFileData *bfd = NULL;
479         int fd;
480
481         fd = open(fileName, O_RDONLY | O_BINARY);
482         if (fd==-1) {
483                 *error_r= BRE_UNABLE_TO_OPEN;
484         } else {
485                 bfd = BLO_readblenfilehandle(fd, error_r);
486         }
487
488         if (fd!=-1)
489                 close(fd);
490
491         return bfd;
492 }
493
494         /* Runtime reading */
495
496 static int handle_read_msb_int(int handle) {
497         unsigned char buf[4];
498
499         if (read(handle, buf, 4)!=4)
500                 return -1;
501         else
502                 return (buf[0]<<24) + (buf[1]<<16) + (buf[2]<<8) + (buf[3]<<0);
503 }
504
505 int blo_is_a_runtime(char *path) {
506         int res= 0, fd= open(path, O_BINARY|O_RDONLY, 0);
507         int datastart;
508         char buf[8];
509
510         if (fd==-1)
511                 goto cleanup;
512         
513         lseek(fd, -12, SEEK_END);
514         
515         datastart= handle_read_msb_int(fd);
516         if (datastart==-1)
517                 goto cleanup;
518         else if (read(fd, buf, 8)!=8)
519                 goto cleanup;
520         else if (memcmp(buf, "BRUNTIME", 8)!=0)
521                 goto cleanup;
522         else
523                 res= 1;
524
525 cleanup:
526         if (fd!=-1)
527                 close(fd);
528
529         return res;     
530 }
531
532 BlendFileData *
533 blo_read_runtime(
534         char *path, 
535         BlendReadError *error_r) 
536 {
537         static char *functionality_check= "\0FUNCTIONALITY_CHECK += BLO_read_runtime\n";
538         BlendFileData *bfd= NULL;
539         int fd, datastart;
540         char buf[8];
541
542         fd= open(path, O_BINARY|O_RDONLY, 0);
543         if (fd==-1) {
544                 *error_r= BRE_UNABLE_TO_OPEN;
545                 goto cleanup;
546         }
547
548         lseek(fd, -12, SEEK_END);
549
550         datastart= handle_read_msb_int(fd);
551         if (datastart==-1) {
552                 *error_r= BRE_UNABLE_TO_READ;
553                 goto cleanup;
554         } else if (read(fd, buf, 8)!=8) {
555                 *error_r= BRE_UNABLE_TO_READ;
556                 goto cleanup;
557         } else if (memcmp(buf, "BRUNTIME", 8)!=0) {
558                 *error_r= BRE_NOT_A_BLEND;
559                 goto cleanup;
560         } else {        
561                 lseek(fd, datastart, SEEK_SET);
562                 bfd= BLO_readblenfilehandle(fd, error_r);
563         }
564         
565 cleanup:
566         if (fd!=-1)
567                 close(fd);
568         
569         return bfd;
570 }
571
572 #if 0
573 static char *brs_error_to_string(int err) {
574         int errFunction = BRS_GETFUNCTION(err);
575         int errGeneric =  BRS_GETGENERR(err);
576         int errSpecific = BRS_GETSPECERR(err);
577         char *errFunctionStrings[] = {
578                 "",
579                 "The read stream",
580                 "The read stream loopback",
581                 "The key store",
582                 "The file reading",
583                 "Decompressing the file",
584                 "Decrypting the file",
585                 "Verifying the signature"};
586         char *errGenericStrings[] = {
587                 "",
588                 "generated an out of memory error",
589                 "bumped on an internal programming error",
590                 "did not recognize this as a blend file",
591                 "failed a blend file check",
592                 "bumped on corrupted data",
593                 "needed the rest of the blend file",
594                 "is not allowed in this version"};
595         char *errReadStreamGlueStrings[] = {
596                 "",
597                 "does not know how to proceed"};
598         char *errReadStreamFileStrings[] = {
599                 "",
600                 "did not recognize this as a blend file",
601                 "was busted on a read error"};
602         char *errInflateStrings[] = {
603                 "",
604                 "bumped on a decompress error"};
605         char *errDecryptStrings[] = {
606                 "",
607                 "could not make a new key",
608                 "bumped on a decrypt error",
609                 "was not allowed. This blend file is not made by you."};
610         char *errVerifyStrings[] = {
611                 "",
612                 "could not make a new key",
613                 "failed"};
614         char *errFunctionString= errFunctionStrings[errFunction];
615         char *errExtraString= "";
616         char *errString;
617         
618         if (errGeneric) {
619                 errExtraString= errGenericStrings[errGeneric];
620         } else if (errSpecific) {
621                 switch (errFunction) {
622                 case BRS_READSTREAMGLUE:
623                         errExtraString= errReadStreamGlueStrings[errSpecific];
624                         break;
625                 case BRS_READSTREAMFILE:
626                         errExtraString= errReadStreamFileStrings[errSpecific];
627                         break;
628                 case BRS_INFLATE:
629                         errExtraString= errInflateStrings[errSpecific];
630                         break;
631                 case BRS_DECRYPT:
632                         errExtraString= errDecryptStrings[errSpecific];
633                         break;
634                 case BRS_VERIFY:
635                         errExtraString= errVerifyStrings[errSpecific];
636                         break;
637                 default:
638                         break;
639                 }
640         }
641         
642         errString= MEM_mallocN(strlen(errFunctionString) + 1 + strlen(errExtraString) + 1);
643         sprintf(errString, "%s %s", errFunctionString, errExtraString);
644         
645         return errString;
646 }
647 #endif