]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - Tools/CCode/Source/FwImage/fwimage.c
Fix building error in Linux
[mirror_edk2.git] / Tools / CCode / Source / FwImage / fwimage.c
... / ...
CommitLineData
1/*++\r
2\r
3Copyright (c) 2004, Intel Corporation \r
4All rights reserved. This program and the accompanying materials \r
5are licensed and made available under the terms and conditions of the BSD License \r
6which accompanies this distribution. The full text of the license may be found at \r
7http://opensource.org/licenses/bsd-license.php \r
8 \r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
11\r
12Module Name:\r
13\r
14 fwimage.c\r
15\r
16Abstract:\r
17\r
18 Converts a pe32+ image to an FW image type\r
19\r
20--*/\r
21\r
22#include "WinNtInclude.h"\r
23\r
24#ifndef __GNUC__\r
25#include <windows.h>\r
26#endif\r
27#include <stdio.h>\r
28#include <stdlib.h>\r
29#include <string.h>\r
30#include <time.h>\r
31\r
32#include <Common/UefiBaseTypes.h>\r
33#include <Common/EfiImage.h>\r
34\r
35#include "CommonLib.h"\r
36#include "EfiUtilityMsgs.c"\r
37\r
38//\r
39// Version of this utility\r
40//\r
41#define UTILITY_NAME "FwImage"\r
42#define UTILITY_MAJOR_VERSION 1\r
43#define UTILITY_MINOR_VERSION 0\r
44\r
45#ifdef __GNUC__\r
46typedef unsigned long ULONG;\r
47typedef unsigned char UCHAR;\r
48typedef unsigned char *PUCHAR;\r
49typedef unsigned short USHORT;\r
50#endif\r
51\r
52static\r
53void\r
54Version (\r
55 VOID\r
56 )\r
57{\r
58 printf ("%s v%d.%d -EDK Utility for Converting a pe32+ image to an FW image type.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION);\r
59 printf ("Copyright (c) 1999-2006 Intel Corporation. All rights reserved.\n");\r
60}\r
61\r
62\r
63VOID\r
64Usage (\r
65 VOID\r
66 )\r
67{\r
68 Version();\r
69 printf ("\nUsage: " UTILITY_NAME " {-t time-date} {-h|--help|-?|/?|-V|--version} \n\\r
70 [BASE|SEC|PEI_CORE|PEIM|DXE_CORE|DXE_DRIVER|DXE_RUNTIME_DRIVER|\n\\r
71 DXE_SAL_DRIVER|DXE_SMM_DRIVER|TOOL|UEFI_DRIVER|UEFI_APPLICATION|\n\\r
72 USER_DEFINED] peimage [outimage]");\r
73}\r
74\r
75static\r
76STATUS\r
77FCopyFile (\r
78 FILE *in,\r
79 FILE *out\r
80 )\r
81{\r
82 ULONG filesize;\r
83 ULONG offset;\r
84 ULONG length;\r
85 UCHAR Buffer[8 * 1024];\r
86\r
87 fseek (in, 0, SEEK_END);\r
88 filesize = ftell (in);\r
89\r
90 fseek (in, 0, SEEK_SET);\r
91 fseek (out, 0, SEEK_SET);\r
92\r
93 offset = 0;\r
94 while (offset < filesize) {\r
95 length = sizeof (Buffer);\r
96 if (filesize - offset < length) {\r
97 length = filesize - offset;\r
98 }\r
99\r
100 fread (Buffer, length, 1, in);\r
101 fwrite (Buffer, length, 1, out);\r
102 offset += length;\r
103 }\r
104\r
105 if ((ULONG) ftell (out) != filesize) {\r
106 Error (NULL, 0, 0, "write error", NULL);\r
107 return STATUS_ERROR;\r
108 }\r
109\r
110 return STATUS_SUCCESS;\r
111}\r
112\r
113static\r
114STATUS\r
115FReadFile (\r
116 FILE *in,\r
117 VOID **Buffer,\r
118 UINTN *Length\r
119 )\r
120{\r
121 fseek (in, 0, SEEK_END);\r
122 *Length = ftell (in);\r
123 *Buffer = malloc (*Length);\r
124 fseek (in, 0, SEEK_SET);\r
125 fread (*Buffer, *Length, 1, in);\r
126 return STATUS_SUCCESS;\r
127}\r
128\r
129static\r
130STATUS\r
131FWriteFile (\r
132 FILE *out,\r
133 VOID *Buffer,\r
134 UINTN Length\r
135 )\r
136{\r
137 fseek (out, 0, SEEK_SET);\r
138 fwrite (Buffer, Length, 1, out);\r
139 if ((ULONG) ftell (out) != Length) {\r
140 Error (NULL, 0, 0, "write error", NULL);\r
141 return STATUS_ERROR;\r
142 }\r
143 free (Buffer);\r
144 return STATUS_SUCCESS;\r
145}\r
146\r
147int\r
148main (\r
149 int argc,\r
150 char *argv[]\r
151 )\r
152/*++\r
153\r
154Routine Description:\r
155\r
156 Main function.\r
157\r
158Arguments:\r
159\r
160 argc - Number of command line parameters.\r
161 argv - Array of pointers to command line parameter strings.\r
162\r
163Returns:\r
164 STATUS_SUCCESS - Utility exits successfully.\r
165 STATUS_ERROR - Some error occurred during execution.\r
166\r
167--*/\r
168{\r
169 ULONG Type;\r
170 PUCHAR Ext;\r
171 PUCHAR p;\r
172 PUCHAR pe;\r
173 PUCHAR OutImageName;\r
174 UCHAR outname[500];\r
175 FILE *fpIn;\r
176 FILE *fpOut;\r
177 VOID *ZeroBuffer;\r
178 EFI_IMAGE_DOS_HEADER *DosHdr;\r
179 EFI_IMAGE_NT_HEADERS *PeHdr;\r
180 EFI_IMAGE_OPTIONAL_HEADER32 *Optional32;\r
181 EFI_IMAGE_OPTIONAL_HEADER64 *Optional64;\r
182 time_t TimeStamp;\r
183 struct tm TimeStruct;\r
184 EFI_IMAGE_DOS_HEADER BackupDosHdr;\r
185 ULONG Index;\r
186 ULONG Index1;\r
187 ULONG Index2;\r
188 ULONG Index3;\r
189 BOOLEAN TimeStampPresent;\r
190 UINTN AllignedRelocSize;\r
191 UINTN Delta;\r
192 EFI_IMAGE_SECTION_HEADER *SectionHeader;\r
193 UINT8 *FileBuffer;\r
194 UINTN FileLength;\r
195 RUNTIME_FUNCTION *RuntimeFunction;\r
196 UNWIND_INFO *UnwindInfo;\r
197\r
198 SetUtilityName (UTILITY_NAME);\r
199 //\r
200 // Assign to fix compile warning\r
201 //\r
202 OutImageName = NULL;\r
203 Type = 0;\r
204 Ext = 0;\r
205 TimeStamp = 0;\r
206 TimeStampPresent = FALSE;\r
207\r
208 if (argc < 1) {\r
209 Usage();\r
210 return STATUS_ERROR;\r
211 }\r
212 \r
213 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0) ||\r
214 (strcmp(argv[1], "-?") == 0) || (strcmp(argv[1], "/?") == 0)) {\r
215 Usage();\r
216 return STATUS_ERROR;\r
217 }\r
218 \r
219 if ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0)) {\r
220 Version();\r
221 return STATUS_ERROR;\r
222 }\r
223 \r
224 //\r
225 // Look for -t time-date option first. If the time is "0", then\r
226 // skip it.\r
227 //\r
228 if ((argc > 2) && !strcmp (argv[1], "-t")) {\r
229 TimeStampPresent = TRUE;\r
230 if (strcmp (argv[2], "0") != 0) {\r
231 //\r
232 // Convert the string to a value\r
233 //\r
234 memset ((char *) &TimeStruct, 0, sizeof (TimeStruct));\r
235 if (sscanf(\r
236 argv[2], "%d/%d/%d,%d:%d:%d",\r
237 &TimeStruct.tm_mon, /* months since January - [0,11] */\r
238 &TimeStruct.tm_mday, /* day of the month - [1,31] */\r
239 &TimeStruct.tm_year, /* years since 1900 */\r
240 &TimeStruct.tm_hour, /* hours since midnight - [0,23] */\r
241 &TimeStruct.tm_min, /* minutes after the hour - [0,59] */\r
242 &TimeStruct.tm_sec /* seconds after the minute - [0,59] */\r
243 ) != 6) {\r
244 Error (NULL, 0, 0, argv[2], "failed to convert to mm/dd/yyyy,hh:mm:ss format");\r
245 return STATUS_ERROR;\r
246 }\r
247 //\r
248 // Now fixup some of the fields\r
249 //\r
250 TimeStruct.tm_mon--;\r
251 TimeStruct.tm_year -= 1900;\r
252 //\r
253 // Sanity-check values?\r
254 // Convert\r
255 //\r
256 TimeStamp = mktime (&TimeStruct);\r
257 if (TimeStamp == (time_t) - 1) {\r
258 Error (NULL, 0, 0, argv[2], "failed to convert time");\r
259 return STATUS_ERROR;\r
260 }\r
261 }\r
262 //\r
263 // Skip over the args\r
264 //\r
265 argc -= 2;\r
266 argv += 2;\r
267 }\r
268 //\r
269 // Check for enough args\r
270 //\r
271 if (argc < 3) {\r
272 Usage ();\r
273 return STATUS_ERROR;\r
274 }\r
275\r
276 if (argc == 4) {\r
277 OutImageName = argv[3];\r
278 }\r
279 //\r
280 // Get new image type\r
281 //\r
282 p = argv[1];\r
283 if (*p == '/' || *p == '\\') {\r
284 p += 1;\r
285 }\r
286\r
287 if (stricmp (p, "app") == 0 || stricmp (p, "UEFI_APPLICATION") == 0) {\r
288 Type = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION;\r
289 Ext = ".efi";\r
290\r
291 } else if (stricmp (p, "bsdrv") == 0 || stricmp (p, "DXE_DRIVER") == 0) {\r
292 Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;\r
293 Ext = ".efi";\r
294\r
295 } else if (stricmp (p, "rtdrv") == 0 || stricmp (p, "DXE_RUNTIME_DRIVER") == 0) {\r
296 Type = EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;\r
297 Ext = ".efi";\r
298\r
299 } else if (stricmp (p, "rtdrv") == 0 || stricmp (p, "DXE_SAL_DRIVER") == 0) {\r
300 Type = EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER;\r
301 Ext = ".efi";\r
302 } else if (stricmp (p, "SEC") == 0) {\r
303 Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;\r
304 Ext = ".sec";\r
305 } else if (stricmp (p, "peim") == 0 ||\r
306 stricmp (p, "BASE") == 0 ||\r
307 stricmp (p, "PEI_CORE") == 0 ||\r
308 stricmp (p, "PEIM") == 0 ||\r
309 stricmp (p, "DXE_SMM_DRIVER") == 0 ||\r
310 stricmp (p, "TOOL") == 0 ||\r
311 stricmp (p, "UEFI_APPLICATION") == 0 ||\r
312 stricmp (p, "USER_DEFINED") == 0 ||\r
313 stricmp (p, "UEFI_DRIVER") == 0 ||\r
314 stricmp (p, "DXE_CORE") == 0\r
315 ) {\r
316 Type = EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;\r
317 Ext = ".pei";\r
318 } else {\r
319 printf ("%s", p);\r
320 Usage ();\r
321 return STATUS_ERROR;\r
322 }\r
323 //\r
324 // open source file\r
325 //\r
326 fpIn = fopen (argv[2], "rb");\r
327 if (!fpIn) {\r
328 Error (NULL, 0, 0, argv[2], "failed to open input file for reading");\r
329 return STATUS_ERROR;\r
330 }\r
331\r
332 FReadFile (fpIn, (VOID **)&FileBuffer, &FileLength);\r
333\r
334 //\r
335 // Read the dos & pe hdrs of the image\r
336 //\r
337 DosHdr = (EFI_IMAGE_DOS_HEADER *)FileBuffer;\r
338 if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {\r
339 Error (NULL, 0, 0, argv[2], "DOS header signature not found in source image");\r
340 fclose (fpIn);\r
341 return STATUS_ERROR;\r
342 }\r
343\r
344 PeHdr = (EFI_IMAGE_NT_HEADERS *)(FileBuffer + DosHdr->e_lfanew);\r
345 if (PeHdr->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
346 Error (NULL, 0, 0, argv[2], "PE header signature not found in source image");\r
347 fclose (fpIn);\r
348 return STATUS_ERROR;\r
349 }\r
350\r
351 //\r
352 // open output file\r
353 //\r
354 strcpy (outname, argv[2]);\r
355 pe = NULL;\r
356 for (p = outname; *p; p++) {\r
357 if (*p == '.') {\r
358 pe = p;\r
359 }\r
360 }\r
361\r
362 if (!pe) {\r
363 pe = p;\r
364 }\r
365\r
366 strcpy (pe, Ext);\r
367\r
368 if (!OutImageName) {\r
369 OutImageName = outname;\r
370 }\r
371\r
372 fpOut = fopen (OutImageName, "w+b");\r
373 if (!fpOut) {\r
374 Error (NULL, 0, 0, OutImageName, "could not open output file for writing");\r
375 fclose (fpIn);\r
376 return STATUS_ERROR;\r
377 }\r
378\r
379 //\r
380 // Zero all unused fields of the DOS header\r
381 //\r
382 memcpy (&BackupDosHdr, DosHdr, sizeof (EFI_IMAGE_DOS_HEADER));\r
383 memset (DosHdr, 0, sizeof (EFI_IMAGE_DOS_HEADER));\r
384 DosHdr->e_magic = BackupDosHdr.e_magic;\r
385 DosHdr->e_lfanew = BackupDosHdr.e_lfanew;\r
386\r
387 for (Index = sizeof (EFI_IMAGE_DOS_HEADER); Index < (ULONG) DosHdr->e_lfanew; Index++) {\r
388 FileBuffer[Index] = DosHdr->e_cp;\r
389 }\r
390\r
391 //\r
392 // Path the PE header\r
393 //\r
394 PeHdr->OptionalHeader.Subsystem = (USHORT) Type;\r
395 if (TimeStampPresent) {\r
396 PeHdr->FileHeader.TimeDateStamp = (UINT32) TimeStamp;\r
397 }\r
398\r
399 if (PeHdr->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
400 Optional32 = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHdr->OptionalHeader;\r
401 Optional32->MajorLinkerVersion = 0;\r
402 Optional32->MinorLinkerVersion = 0;\r
403 Optional32->MajorOperatingSystemVersion = 0;\r
404 Optional32->MinorOperatingSystemVersion = 0;\r
405 Optional32->MajorImageVersion = 0;\r
406 Optional32->MinorImageVersion = 0;\r
407 Optional32->MajorSubsystemVersion = 0;\r
408 Optional32->MinorSubsystemVersion = 0;\r
409 Optional32->Win32VersionValue = 0;\r
410 Optional32->CheckSum = 0;\r
411 Optional32->SizeOfStackReserve = 0;\r
412 Optional32->SizeOfStackCommit = 0;\r
413 Optional32->SizeOfHeapReserve = 0;\r
414 Optional32->SizeOfHeapCommit = 0;\r
415\r
416 //\r
417 // Strip zero padding at the end of the .reloc section \r
418 //\r
419 if (Optional32->NumberOfRvaAndSizes >= 6) {\r
420 if (Optional32->DataDirectory[5].Size != 0) {\r
421 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
422 for (Index = 0; Index < PeHdr->FileHeader.NumberOfSections; Index++, SectionHeader++) {\r
423 //\r
424 // Look for the Section Header that starts as the same virtual address as the Base Relocation Data Directory\r
425 //\r
426 if (SectionHeader->VirtualAddress == Optional32->DataDirectory[5].VirtualAddress) {\r
427 SectionHeader->Misc.VirtualSize = Optional32->DataDirectory[5].Size;\r
428 AllignedRelocSize = (Optional32->DataDirectory[5].Size + Optional32->FileAlignment - 1) & (~(Optional32->FileAlignment - 1));\r
429 //\r
430 // Check to see if there is zero padding at the end of the base relocations\r
431 //\r
432 if (AllignedRelocSize < SectionHeader->SizeOfRawData) {\r
433 //\r
434 // Check to see if the base relocations are at the end of the file\r
435 //\r
436 if (SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData == Optional32->SizeOfImage) {\r
437 //\r
438 // All the required conditions are met to strip the zero padding of the end of the base relocations section\r
439 //\r
440 Optional32->SizeOfImage -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
441 Optional32->SizeOfInitializedData -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
442 SectionHeader->SizeOfRawData = AllignedRelocSize;\r
443 FileLength = Optional32->SizeOfImage;\r
444 }\r
445 }\r
446 }\r
447 }\r
448 }\r
449 }\r
450 } \r
451 if (PeHdr->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r
452 Optional64 = (EFI_IMAGE_OPTIONAL_HEADER64 *)&PeHdr->OptionalHeader;\r
453 Optional64->MajorLinkerVersion = 0;\r
454 Optional64->MinorLinkerVersion = 0;\r
455 Optional64->MajorOperatingSystemVersion = 0;\r
456 Optional64->MinorOperatingSystemVersion = 0;\r
457 Optional64->MajorImageVersion = 0;\r
458 Optional64->MinorImageVersion = 0;\r
459 Optional64->MajorSubsystemVersion = 0;\r
460 Optional64->MinorSubsystemVersion = 0;\r
461 Optional64->Win32VersionValue = 0;\r
462 Optional64->CheckSum = 0;\r
463 Optional64->SizeOfStackReserve = 0;\r
464 Optional64->SizeOfStackCommit = 0;\r
465 Optional64->SizeOfHeapReserve = 0;\r
466 Optional64->SizeOfHeapCommit = 0;\r
467\r
468 //\r
469 // Zero the .pdata section if the machine type is X64 and the Debug Directory is empty\r
470 //\r
471 if (PeHdr->FileHeader.Machine == 0x8664) { // X64\r
472 if (Optional64->NumberOfRvaAndSizes >= 4) {\r
473 if (Optional64->NumberOfRvaAndSizes < 7 || (Optional64->NumberOfRvaAndSizes >= 7 && Optional64->DataDirectory[6].Size == 0)) {\r
474 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
475 for (Index = 0; Index < PeHdr->FileHeader.NumberOfSections; Index++, SectionHeader++) {\r
476 if (SectionHeader->VirtualAddress == Optional64->DataDirectory[3].VirtualAddress) {\r
477 RuntimeFunction = (RUNTIME_FUNCTION *)(FileBuffer + SectionHeader->PointerToRawData);\r
478 for (Index1 = 0; Index1 < Optional64->DataDirectory[3].Size / sizeof (RUNTIME_FUNCTION); Index1++, RuntimeFunction++) {\r
479 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
480 for (Index2 = 0; Index2 < PeHdr->FileHeader.NumberOfSections; Index2++, SectionHeader++) {\r
481 if (RuntimeFunction->UnwindInfoAddress > SectionHeader->VirtualAddress && RuntimeFunction->UnwindInfoAddress < (SectionHeader->VirtualAddress + SectionHeader->SizeOfRawData)) {\r
482 UnwindInfo = (UNWIND_INFO *)(FileBuffer + SectionHeader->PointerToRawData + (RuntimeFunction->UnwindInfoAddress - SectionHeader->VirtualAddress));\r
483 if (UnwindInfo->Version == 1) {\r
484 memset (UnwindInfo + 1, 0, UnwindInfo->CountOfUnwindCodes * sizeof (UINT16));\r
485 memset (UnwindInfo, 0, sizeof (UNWIND_INFO));\r
486 }\r
487 }\r
488 }\r
489 memset (RuntimeFunction, 0, sizeof (RUNTIME_FUNCTION));\r
490 }\r
491\r
492 break;\r
493 }\r
494 }\r
495 Optional64->DataDirectory[3].Size = 0;\r
496 Optional64->DataDirectory[3].VirtualAddress = 0;\r
497 }\r
498 }\r
499 }\r
500\r
501 //\r
502 // Strip zero padding at the end of the .reloc section \r
503 //\r
504 if (Optional64->NumberOfRvaAndSizes >= 6) {\r
505 if (Optional64->DataDirectory[5].Size != 0) {\r
506 SectionHeader = (EFI_IMAGE_SECTION_HEADER *)(FileBuffer + DosHdr->e_lfanew + sizeof(UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + PeHdr->FileHeader.SizeOfOptionalHeader);\r
507 for (Index = 0; Index < PeHdr->FileHeader.NumberOfSections; Index++, SectionHeader++) {\r
508 //\r
509 // Look for the Section Header that starts as the same virtual address as the Base Relocation Data Directory\r
510 //\r
511 if (SectionHeader->VirtualAddress == Optional64->DataDirectory[5].VirtualAddress) {\r
512 SectionHeader->Misc.VirtualSize = Optional64->DataDirectory[5].Size;\r
513 AllignedRelocSize = (Optional64->DataDirectory[5].Size + Optional64->FileAlignment - 1) & (~(Optional64->FileAlignment - 1));\r
514 //\r
515 // Check to see if there is zero padding at the end of the base relocations\r
516 //\r
517 if (AllignedRelocSize < SectionHeader->SizeOfRawData) {\r
518 //\r
519 // Check to see if the base relocations are at the end of the file\r
520 //\r
521 if (SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData == Optional64->SizeOfImage) {\r
522 //\r
523 // All the required conditions are met to strip the zero padding of the end of the base relocations section\r
524 //\r
525 Optional64->SizeOfImage -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
526 Optional64->SizeOfInitializedData -= (SectionHeader->SizeOfRawData - AllignedRelocSize);\r
527 SectionHeader->SizeOfRawData = AllignedRelocSize;\r
528 FileLength = Optional64->SizeOfImage;\r
529 }\r
530 }\r
531 }\r
532 }\r
533 }\r
534 }\r
535 }\r
536\r
537 FWriteFile (fpOut, FileBuffer, FileLength);\r
538\r
539 //\r
540 // Done\r
541 //\r
542 fclose (fpIn);\r
543 fclose (fpOut);\r
544 //\r
545 // printf ("Created %s\n", OutImageName);\r
546 //\r
547 return STATUS_SUCCESS;\r
548}\r