strcpy() --> BLI_strncpy(), where source strings are not fixed and target size is...
[blender.git] / source / blender / blenkernel / intern / packedFile.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL 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.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 /** \file blender/blenkernel/intern/packedFile.c
31  *  \ingroup bke
32  */
33
34
35 #include <stdio.h>
36 #include <fcntl.h>
37 #include <sys/stat.h>
38
39 #ifndef WIN32 
40 #include <unistd.h>
41 #else
42 #include <io.h>
43 #endif
44 #include <string.h>
45 #include "MEM_guardedalloc.h"
46
47 #include "DNA_image_types.h"
48 #include "DNA_sound_types.h"
49 #include "DNA_vfont_types.h"
50 #include "DNA_packedFile_types.h"
51
52 #include "BLI_blenlib.h"
53 #include "BLI_utildefines.h"
54
55 #include "BKE_utildefines.h"
56 #include "BKE_global.h"
57 #include "BKE_main.h"
58 #include "BKE_sound.h"
59 #include "BKE_image.h"
60 #include "BKE_packedFile.h"
61 #include "BKE_report.h"
62
63 #ifdef _WIN32
64 #define open _open
65 #define close _close
66 #define read _read
67 #define write _write
68 #endif
69
70
71 int seekPackedFile(PackedFile *pf, int offset, int whence)
72 {
73         int oldseek = -1, seek = 0;
74
75         if (pf) {
76                 oldseek = pf->seek;
77                 switch(whence) {
78                 case SEEK_CUR:
79                         seek = oldseek + offset;
80                         break;
81                 case SEEK_END:
82                         seek = pf->size + offset;
83                         break;
84                 case SEEK_SET:
85                         seek = offset;
86                         break;
87                 default:
88                         oldseek = -1;
89                 }
90                 if (seek < 0) {
91                         seek = 0;
92                 } else if (seek > pf->size) {
93                         seek = pf->size;
94                 }
95                 pf->seek = seek;
96         }
97
98         return(oldseek);
99 }
100         
101 void rewindPackedFile(PackedFile *pf)
102 {
103         seekPackedFile(pf, 0, SEEK_SET);
104 }
105
106 int readPackedFile(PackedFile *pf, void *data, int size)
107
108         if ((pf != NULL) && (size >= 0) && (data != NULL)) {
109                 if (size + pf->seek > pf->size) {
110                         size = pf->size - pf->seek;
111                 }
112
113                 if (size > 0) {
114                         memcpy(data, ((char *) pf->data) + pf->seek, size);
115                 } else {
116                         size = 0;
117                 }
118
119                 pf->seek += size;
120         } else {
121                 size = -1;
122         }
123
124         return(size);
125 }
126
127 int countPackedFiles(Main *bmain)
128 {
129         Image *ima;
130         VFont *vf;
131         bSound *sound;
132         int count = 0;
133         
134         // let's check if there are packed files...
135         for(ima=bmain->image.first; ima; ima=ima->id.next)
136                 if(ima->packedfile)
137                         count++;
138
139         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
140                 if(vf->packedfile)
141                         count++;
142
143         for(sound=bmain->sound.first; sound; sound=sound->id.next)
144                 if(sound->packedfile)
145                         count++;
146
147         return count;
148 }
149
150 void freePackedFile(PackedFile *pf)
151 {
152         if(pf) {
153                 MEM_freeN(pf->data);
154                 MEM_freeN(pf);
155         }
156         else
157                 printf("freePackedFile: Trying to free a NULL pointer\n");
158 }
159         
160 PackedFile *newPackedFileMemory(void *mem, int memlen)
161 {
162         PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
163         pf->data = mem;
164         pf->size = memlen;
165         
166         return pf;
167 }
168
169 PackedFile *newPackedFile(ReportList *reports, const char *filename, const char *basepath)
170 {
171         PackedFile *pf = NULL;
172         int file, filelen;
173         char name[FILE_MAXDIR+FILE_MAXFILE];
174         void *data;
175         
176         /* render result has no filename and can be ignored
177          * any other files with no name can be ignored too */
178         if(filename[0]=='\0')
179                 return NULL;
180
181         //XXX waitcursor(1);
182         
183         // convert relative filenames to absolute filenames
184         
185         BLI_strncpy(name, filename, sizeof(name));
186         BLI_path_abs(name, basepath);
187         
188         // open the file
189         // and create a PackedFile structure
190
191         file= open(name, O_BINARY|O_RDONLY);
192         if (file <= 0) {
193                 BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path not found: \"%s\"", name);
194         } else {
195                 filelen = BLI_filesize(file);
196
197                 if (filelen == 0) {
198                         // MEM_mallocN complains about MEM_mallocN(0, "bla");
199                         // we don't care....
200                         data = MEM_mallocN(1, "packFile");
201                 } else {
202                         data = MEM_mallocN(filelen, "packFile");
203                 }
204                 if (read(file, data, filelen) == filelen) {
205                         pf = newPackedFileMemory(data, filelen);
206                 }
207
208                 close(file);
209         }
210
211         //XXX waitcursor(0);
212                 
213         return (pf);
214 }
215
216 void packAll(Main *bmain, ReportList *reports)
217 {
218         Image *ima;
219         VFont *vf;
220         bSound *sound;
221         
222         for(ima=bmain->image.first; ima; ima=ima->id.next) {
223                 if(ima->packedfile == NULL && ima->id.lib==NULL) { 
224                         if(ima->source==IMA_SRC_FILE) {
225                                 ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id));
226                         }
227                         else if(ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
228                                 BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported.", ima->id.name+2);
229                         }
230                 }
231         }
232
233         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
234                 if(vf->packedfile == NULL && vf->id.lib==NULL && strcmp(vf->name, FO_BUILTIN_NAME) != 0)
235                         vf->packedfile = newPackedFile(reports, vf->name, bmain->name);
236
237         for(sound=bmain->sound.first; sound; sound=sound->id.next)
238                 if(sound->packedfile == NULL && sound->id.lib==NULL)
239                         sound->packedfile = newPackedFile(reports, sound->name, bmain->name);
240 }
241
242
243 #if 0
244
245 // attempt to create a function that generates an unique filename
246 // this will work when all funtions in fileops.c understand relative filenames...
247
248 static char *find_new_name(char *name)
249 {
250         char tempname[FILE_MAXDIR + FILE_MAXFILE];
251         char *newname;
252         size_t len;
253         
254         if (fop_exists(name)) {
255                 for (number = 1; number <= 999; number++) {
256                         sprintf(tempname, "%s.%03d", name, number);
257                         if (! fop_exists(tempname)) {
258                                 break;
259                         }
260                 }
261         }
262         len= strlen(tempname) + 1;
263         newname = MEM_mallocN(len, "find_new_name");
264         memcpy(newname, tempname, len * sizeof(char));
265         return newname;
266 }
267 #endif
268
269 int writePackedFile(ReportList *reports, const char *filename, PackedFile *pf, int guimode)
270 {
271         int file, number, remove_tmp = FALSE;
272         int ret_value = RET_OK;
273         char name[FILE_MAXDIR + FILE_MAXFILE];
274         char tempname[FILE_MAXDIR + FILE_MAXFILE];
275 /*      void *data; */
276         
277         if (guimode) {} //XXX  waitcursor(1);
278         
279         BLI_strncpy(name, filename, sizeof(name));
280         BLI_path_abs(name, G.main->name);
281         
282         if (BLI_exists(name)) {
283                 for (number = 1; number <= 999; number++) {
284                         BLI_snprintf(tempname, sizeof(tempname), "%s.%03d_", name, number);
285                         if (! BLI_exists(tempname)) {
286                                 if (BLI_copy_fileops(name, tempname) == RET_OK) {
287                                         remove_tmp = TRUE;
288                                 }
289                                 break;
290                         }
291                 }
292         }
293         
294         // make sure the path to the file exists...
295         BLI_make_existing_file(name);
296         
297         file = open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
298         if (file >= 0) {
299                 if (write(file, pf->data, pf->size) != pf->size) {
300                         BKE_reportf(reports, RPT_ERROR, "Error writing file: %s", name);
301                         ret_value = RET_ERROR;
302                 }
303                 close(file);
304         } else {
305                 BKE_reportf(reports, RPT_ERROR, "Error creating file: %s", name);
306                 ret_value = RET_ERROR;
307         }
308         
309         if (remove_tmp) {
310                 if (ret_value == RET_ERROR) {
311                         if (BLI_rename(tempname, name) != 0) {
312                                 BKE_reportf(reports, RPT_ERROR, "Error restoring tempfile. Check files: '%s' '%s'", tempname, name);
313                         }
314                 } else {
315                         if (BLI_delete(tempname, 0, 0) != 0) {
316                                 BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
317                         }
318                 }
319         }
320         
321         if(guimode) {} //XXX waitcursor(0);
322
323         return (ret_value);
324 }
325         
326 /* 
327
328 This function compares a packed file to a 'real' file.
329 It returns an integer indicating if:
330
331 PF_EQUAL                - the packed file and original file are identical
332 PF_DIFFERENT    - the packed file and original file differ
333 PF_NOFILE               - the original file doens't exist
334
335 */
336
337 int checkPackedFile(const char *filename, PackedFile *pf)
338 {
339         struct stat st;
340         int ret_val, i, len, file;
341         char buf[4096];
342         char name[FILE_MAXDIR + FILE_MAXFILE];
343         
344         BLI_strncpy(name, filename, sizeof(name));
345         BLI_path_abs(name, G.main->name);
346         
347         if (stat(name, &st)) {
348                 ret_val = PF_NOFILE;
349         } else if (st.st_size != pf->size) {
350                 ret_val = PF_DIFFERS;
351         } else {
352                 // we'll have to compare the two...
353                 
354                 file = open(name, O_BINARY | O_RDONLY);
355                 if (file < 0) {
356                         ret_val = PF_NOFILE;
357                 } else {
358                         ret_val = PF_EQUAL;
359                         
360                         for (i = 0; i < pf->size; i += sizeof(buf)) {
361                                 len = pf->size - i;
362                                 if (len > sizeof(buf)) {
363                                         len = sizeof(buf);
364                                 }
365                                 
366                                 if (read(file, buf, len) != len) {
367                                         // read error ...
368                                         ret_val = PF_DIFFERS;
369                                         break;
370                                 } else {
371                                         if (memcmp(buf, ((char *)pf->data) + i, len)) {
372                                                 ret_val = PF_DIFFERS;
373                                                 break;
374                                         }
375                                 }
376                         }
377                         
378                         close(file);
379                 }
380         }
381         
382         return(ret_val);
383 }
384
385 /*
386
387    unpackFile() looks at the existing files (abs_name, local_name) and a packed file.
388
389 It returns a char *to the existing file name / new file name or NULL when
390 there was an error or when the user desides to cancel the operation.
391
392 */
393
394 char *unpackFile(ReportList *reports, const char *abs_name, const char *local_name, PackedFile *pf, int how)
395 {
396         char *newname = NULL;
397         const char *temp = NULL;
398         
399         // char newabs[FILE_MAXDIR + FILE_MAXFILE];
400         // char newlocal[FILE_MAXDIR + FILE_MAXFILE];
401         
402         if (pf != NULL) {
403                 switch (how) {
404                         case -1:
405                         case PF_KEEP:
406                                 break;
407                         case PF_REMOVE:
408                                 temp= abs_name;
409                                 break;
410                         case PF_USE_LOCAL:
411                                 // if file exists use it
412                                 if (BLI_exists(local_name)) {
413                                         temp = local_name;
414                                         break;
415                                 }
416                                 // else fall through and create it
417                         case PF_WRITE_LOCAL:
418                                 if (writePackedFile(reports, local_name, pf, 1) == RET_OK) {
419                                         temp = local_name;
420                                 }
421                                 break;
422                         case PF_USE_ORIGINAL:
423                                 // if file exists use it
424                                 if (BLI_exists(abs_name)) {
425                                         temp = abs_name;
426                                         break;
427                                 }
428                                 // else fall through and create it
429                         case PF_WRITE_ORIGINAL:
430                                 if (writePackedFile(reports, abs_name, pf, 1) == RET_OK) {
431                                         temp = abs_name;
432                                 }
433                                 break;
434                         default:
435                                 printf("unpackFile: unknown return_value %d\n", how);
436                                 break;
437                 }
438                 
439                 if (temp) {
440                         newname= BLI_strdup(temp);
441                 }
442         }
443         
444         return newname;
445 }
446
447
448 int unpackVFont(ReportList *reports, VFont *vfont, int how)
449 {
450         char localname[FILE_MAXDIR + FILE_MAXFILE], fi[FILE_MAXFILE];
451         char *newname;
452         int ret_value = RET_ERROR;
453         
454         if (vfont != NULL) {
455                 BLI_strncpy(localname, vfont->name, sizeof(localname));
456                 BLI_splitdirstring(localname, fi);
457                 
458                 BLI_snprintf(localname, sizeof(localname), "//fonts/%s", fi);
459                 
460                 newname = unpackFile(reports, vfont->name, localname, vfont->packedfile, how);
461                 if (newname != NULL) {
462                         ret_value = RET_OK;
463                         freePackedFile(vfont->packedfile);
464                         vfont->packedfile = NULL;
465                         BLI_strncpy(vfont->name, newname, sizeof(vfont->name));
466                         MEM_freeN(newname);
467                 }
468         }
469         
470         return (ret_value);
471 }
472
473 int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how)
474 {
475         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
476         char *newname;
477         int ret_value = RET_ERROR;
478
479         if (sound != NULL) {
480                 BLI_strncpy(localname, sound->name, sizeof(localname));
481                 BLI_splitdirstring(localname, fi);
482                 BLI_snprintf(localname, sizeof(localname), "//sounds/%s", fi);
483
484                 newname = unpackFile(reports, sound->name, localname, sound->packedfile, how);
485                 if (newname != NULL) {
486                         BLI_strncpy(sound->name, newname, sizeof(sound->name));
487                         MEM_freeN(newname);
488
489                         freePackedFile(sound->packedfile);
490                         sound->packedfile = NULL;
491
492                         sound_load(bmain, sound);
493
494                         ret_value = RET_OK;
495                 }
496         }
497         
498         return(ret_value);
499 }
500
501 int unpackImage(ReportList *reports, Image *ima, int how)
502 {
503         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
504         char *newname;
505         int ret_value = RET_ERROR;
506         
507         if (ima != NULL) {
508                 BLI_strncpy(localname, ima->name, sizeof(localname));
509                 BLI_splitdirstring(localname, fi);
510                 BLI_snprintf(localname, sizeof(localname), "//textures/%s", fi);
511
512                 newname = unpackFile(reports, ima->name, localname, ima->packedfile, how);
513                 if (newname != NULL) {
514                         ret_value = RET_OK;
515                         freePackedFile(ima->packedfile);
516                         ima->packedfile = NULL;
517                         BLI_strncpy(ima->name, newname, sizeof(ima->name));
518                         MEM_freeN(newname);
519                         BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
520                 }
521         }
522         
523         return(ret_value);
524 }
525
526 void unpackAll(Main *bmain, ReportList *reports, int how)
527 {
528         Image *ima;
529         VFont *vf;
530         bSound *sound;
531
532         for(ima=bmain->image.first; ima; ima=ima->id.next)
533                 if(ima->packedfile)
534                         unpackImage(reports, ima, how);
535
536         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
537                 if(vf->packedfile)
538                         unpackVFont(reports, vf, how);
539
540         for(sound=bmain->sound.first; sound; sound=sound->id.next)
541                 if(sound->packedfile)
542                         unpackSound(bmain, reports, sound, how);
543 }
544