blob: d9e3e758177f1c5dba606540ee423ed23f09fa18 [file] [log] [blame]
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009 by Maurus Cuelenaere
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdio.h>
22#include <stdarg.h>
23#include <stddef.h>
24#include <stdlib.h>
25#include <stdint.h>
26#include <string.h>
27#include <time.h>
28#include "chinachip.h"
29
30/* From http://www.rockbox.org/wiki/ChinaChip */
31struct header
32{
33 uint32_t signature; /* WADF */
34 uint32_t unk;
35 int8_t timestamp[12]; /* 200805081100 */
36 uint32_t size;
37 uint32_t checksum;
38 uint32_t unk2;
39 int8_t identifier[32]; /* Chinachip PMP firmware V1.0 */
40} __attribute__ ((packed));
41
42static inline void int2le(unsigned char* addr, unsigned int val)
43{
44 addr[0] = val & 0xff;
45 addr[1] = (val >> 8) & 0xff;
46 addr[2] = (val >> 16) & 0xff;
47 addr[3] = (val >> 24) & 0xff;
48}
49
50static inline unsigned int le2int(unsigned char* buf)
51{
52 return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
53}
54
55static long int filesize(FILE* fd)
56{
57 long int len;
58 fseek(fd, 0, SEEK_END);
59 len = ftell(fd);
60 fseek(fd, 0, SEEK_SET);
61 return len;
62}
63
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +000064#define FCLOSE(fd) fclose(fd); fd = NULL;
65#define CCPMPBIN_HEADER_SIZE (sizeof(uint32_t)*2 + sizeof(uint8_t) + 9)
66#define TOTAL_SIZE (fsize + CCPMPBIN_HEADER_SIZE + bsize)
Dominik Riebeling059cb712011-12-03 09:41:44 +000067enum cc_error chinachip_patch(const char* firmware, const char* bootloader,
68 const char* output, const char* ccpmp_backup)
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +000069{
70 char header_time[13];
71 time_t cur_time;
72 struct tm* time_info;
73 unsigned char* buf = NULL;
74 FILE *fd = NULL, *bd = NULL, *od = NULL;
75 unsigned int ccpmp_size = 0, i, fsize, bsize;
76 signed int checksum = 0, ccpmp_pos;
Dominik Riebeling059cb712011-12-03 09:41:44 +000077 int result = E_OK;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +000078
79 fd = fopen(firmware, "rb");
80 if(!fd)
81 {
Dominik Riebeling059cb712011-12-03 09:41:44 +000082 fprintf(stderr, "[ERR] Can't open file %s!\n", firmware);
83 result = E_OPEN_FIRMWARE;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +000084 goto err;
85 }
86 bd = fopen(bootloader, "rb");
87 if(!bd)
88 {
Dominik Riebeling059cb712011-12-03 09:41:44 +000089 fprintf(stderr, "[ERR] Can't open file %s!\n", bootloader);
90 result = E_OPEN_BOOTLOADER;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +000091 goto err;
92 }
93
94 bsize = filesize(bd);
Dominik Riebeling059cb712011-12-03 09:41:44 +000095 fprintf(stderr, "[INFO] Bootloader size is %d bytes\n", bsize);
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +000096 FCLOSE(bd);
97
98 fsize = filesize(fd);
Dominik Riebeling059cb712011-12-03 09:41:44 +000099 fprintf(stderr, "[INFO] Firmware size is %d bytes\n", fsize);
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000100
101 buf = malloc(TOTAL_SIZE);
102 if(buf == NULL)
103 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000104 fprintf(stderr, "[ERR] Can't allocate %d bytes!\n", fsize);
105 result = E_MEMALLOC;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000106 goto err;
107 }
108 memset(buf, 0, TOTAL_SIZE);
109
Dominik Riebeling059cb712011-12-03 09:41:44 +0000110 fprintf(stderr, "[INFO] Reading %s into memory...\n", firmware);
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000111 if(fread(buf, fsize, 1, fd) != 1)
112 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000113 fprintf(stderr, "[ERR] Can't read file %s to memory!\n", firmware);
114 result = E_LOAD_FIRMWARE;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000115 goto err;
116 }
117 FCLOSE(fd);
118
119 if(memcmp(buf, "WADF", 4))
120 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000121 fprintf(stderr, "[ERR] File %s isn't a valid ChinaChip firmware!\n", firmware);
122 result = E_INVALID_FILE;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000123 goto err;
124 }
125
126 ccpmp_pos = -1, i = 0x40;
127 do
128 {
129 int filenamesize = le2int(&buf[i]);
130 i += sizeof(uint32_t);
131
132 if(!strncmp((char*) &buf[i], "ccpmp.bin", 9))
133 {
134 ccpmp_pos = i;
135 ccpmp_size = le2int(&buf[i + sizeof(uint8_t) + filenamesize]);
136 }
137 else
138 i += filenamesize + le2int(&buf[i + sizeof(uint8_t) + filenamesize])
139 + sizeof(uint32_t) + sizeof(uint8_t);
140 } while(ccpmp_pos < 0 && i < fsize);
141
142 if(i >= fsize)
143 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000144 fprintf(stderr, "[ERR] Couldn't find ccpmp.bin in %s!\n", firmware);
145 result = E_NO_CCPMP;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000146 goto err;
147 }
Dominik Riebeling059cb712011-12-03 09:41:44 +0000148 fprintf(stderr, "[INFO] Found ccpmp.bin at %d bytes\n", ccpmp_pos);
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000149
150 if(ccpmp_backup)
151 {
Maurus Cuelenaere98f5c302009-09-14 12:29:34 +0000152 int ccpmp_data_pos = ccpmp_pos + 9;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000153 bd = fopen(ccpmp_backup, "wb");
154 if(!bd)
155 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000156 fprintf(stderr, "[ERR] Can't open file %s!\n", ccpmp_backup);
157 result = E_OPEN_BACKUP;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000158 goto err;
159 }
160
Dominik Riebeling059cb712011-12-03 09:41:44 +0000161 fprintf(stderr, "[INFO] Writing %d bytes to %s...\n", ccpmp_size, ccpmp_backup);
Maurus Cuelenaere98f5c302009-09-14 12:29:34 +0000162 if(fwrite(&buf[ccpmp_data_pos], ccpmp_size, 1, bd) != 1)
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000163 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000164 fprintf(stderr, "[ERR] Can't write to file %s!\n", ccpmp_backup);
165 result = E_WRITE_BACKUP;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000166 goto err;
167 }
168 FCLOSE(bd);
169 }
170
Dominik Riebeling059cb712011-12-03 09:41:44 +0000171 fprintf(stderr, "[INFO] Renaming it to ccpmp.old...\n");
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000172 buf[ccpmp_pos + 6] = 'o';
173 buf[ccpmp_pos + 7] = 'l';
174 buf[ccpmp_pos + 8] = 'd';
175
176 bd = fopen(bootloader, "rb");
177 if(!bd)
178 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000179 fprintf(stderr, "[ERR] Can't open file %s!\n", bootloader);
180 result = E_OPEN_BOOTLOADER;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000181 goto err;
182 }
183
184 /* Also include path size */
185 ccpmp_pos -= sizeof(uint32_t);
186
Dominik Riebeling059cb712011-12-03 09:41:44 +0000187 fprintf(stderr, "[INFO] Making place for ccpmp.bin...\n");
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000188 memmove(&buf[ccpmp_pos + bsize + CCPMPBIN_HEADER_SIZE],
189 &buf[ccpmp_pos], fsize - ccpmp_pos);
190
Dominik Riebeling059cb712011-12-03 09:41:44 +0000191 fprintf(stderr, "[INFO] Reading %s into memory...\n", bootloader);
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000192 if(fread(&buf[ccpmp_pos + CCPMPBIN_HEADER_SIZE],
193 bsize, 1, bd) != 1)
194 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000195 fprintf(stderr, "[ERR] Can't read file %s to memory!\n", bootloader);
196 result = E_LOAD_BOOTLOADER;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000197 goto err;
198 }
199 FCLOSE(bd);
200
Dominik Riebeling059cb712011-12-03 09:41:44 +0000201 fprintf(stderr, "[INFO] Adding header to %s...\n", bootloader);
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000202 int2le(&buf[ccpmp_pos ], 9); /* Pathname Size */
203 memcpy(&buf[ccpmp_pos + 4 ], "ccpmp.bin", 9); /* Pathname */
204 memset(&buf[ccpmp_pos + 4 + 9 ], 0x20, sizeof(uint8_t)); /* File Type */
205 int2le(&buf[ccpmp_pos + 4 + 9 + 1], bsize); /* File Size */
206
207 time(&cur_time);
208 time_info = localtime(&cur_time);
209 if(time_info == NULL)
210 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000211 fprintf(stderr, "[ERR] Can't obtain current time!\n");
212 result = E_GET_TIME;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000213 goto err;
214 }
215
216 snprintf(header_time, 13, "%04d%02d%02d%02d%02d", time_info->tm_year + 1900,
217 time_info->tm_mon,
218 time_info->tm_mday,
219 time_info->tm_hour,
220 time_info->tm_min);
221
Dominik Riebeling059cb712011-12-03 09:41:44 +0000222 fprintf(stderr, "[INFO] Computing checksum...\n");
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000223 for(i = sizeof(struct header); i < TOTAL_SIZE; i+=4)
224 checksum += le2int(&buf[i]);
225
Dominik Riebeling059cb712011-12-03 09:41:44 +0000226 fprintf(stderr, "[INFO] Updating main header...\n");
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000227 memcpy(&buf[offsetof(struct header, timestamp)], header_time, 12);
228 int2le(&buf[offsetof(struct header, size) ], TOTAL_SIZE);
229 int2le(&buf[offsetof(struct header, checksum) ], checksum);
230
231 od = fopen(output, "wb");
232 if(!od)
233 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000234 fprintf(stderr, "[ERR] Can't open file %s!\n", output);
235 result = E_OPEN_OUTFILE;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000236 goto err;
237 }
238
Dominik Riebeling059cb712011-12-03 09:41:44 +0000239 fprintf(stderr, "[INFO] Writing output to %s...\n", output);
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000240 if(fwrite(buf, TOTAL_SIZE, 1, od) != 1)
241 {
Dominik Riebeling059cb712011-12-03 09:41:44 +0000242 fprintf(stderr, "[ERR] Can't write to file %s!\n", output);
243 result = E_WRITE_OUTFILE;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000244 goto err;
245 }
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000246
247err:
248 if(buf)
249 free(buf);
250 if(fd)
251 fclose(fd);
252 if(bd)
253 fclose(bd);
254 if(od)
255 fclose(od);
256
Dominik Riebeling059cb712011-12-03 09:41:44 +0000257 return result;
Maurus Cuelenaeree8c71aa2009-08-16 20:39:00 +0000258}
259