convert.c (8428B)
1 /* 2 This file contains code related to cube format conversion that used to 3 be in src/core. It is to be adapted into a standalone tool for cube 4 format conversion. 5 */ 6 7 STATIC cube_t readcube(const char *, const char *); 8 STATIC int64_t writecube(const char *, cube_t, size_t n, char [n]); 9 STATIC void log_available_formats(void); 10 STATIC uint8_t readco(const char *); 11 STATIC uint8_t readcp(const char *); 12 STATIC uint8_t readeo(const char *); 13 STATIC uint8_t readep(const char *); 14 STATIC cube_t readcube_B32(const char *); 15 STATIC cube_t readcube_H48(const char *); 16 STATIC uint8_t readpiece_LST(const char **); 17 STATIC cube_t readcube_LST(const char *); 18 19 STATIC int64_t writepiece_LST(uint8_t, size_t n, char [n]); 20 STATIC int64_t writecube_B32(cube_t, size_t n, char [n]); 21 STATIC int64_t writecube_H48(cube_t, size_t n, char [n]); 22 STATIC int64_t writecube_LST(cube_t, size_t n, char [n]); 23 24 STATIC uint8_t b32toedge(char); 25 STATIC uint8_t b32tocorner(char); 26 STATIC char edgetob32(uint8_t); 27 STATIC char cornertob32(uint8_t); 28 29 STATIC struct { 30 const char *name; 31 cube_t (*read)(const char *); 32 int64_t (*write)(cube_t, size_t n, char [n]); 33 } ioformat[] = 34 { 35 { .name = "B32", .read = readcube_B32, .write = writecube_B32 }, 36 { .name = "LST", .read = readcube_LST, .write = writecube_LST }, 37 { .name = "H48", .read = readcube_H48, .write = writecube_H48 }, 38 { .name = "NONE", .read = NULL, .write = NULL }, 39 }; 40 41 STATIC cube_t 42 readcube(const char *format, const char *buf) 43 { 44 int i; 45 46 for (i = 0; ioformat[i].read != NULL; i++) 47 if (!strcmp(format, ioformat[i].name)) 48 return ioformat[i].read(buf); 49 50 LOG("Cannot read cube: unknown format '%s'\n", format); 51 log_available_formats(); 52 return ZERO_CUBE; 53 } 54 55 STATIC int64_t 56 writecube(const char *format, cube_t cube, size_t buf_size, char buf[buf_size]) 57 { 58 int i; 59 60 for (i = 0; ioformat[i].write != NULL; i++) 61 if (!strcmp(format, ioformat[i].name)) 62 return ioformat[i].write(cube, buf_size, buf); 63 64 LOG("Cannot write cube: unknown format '%s'\n", format); 65 log_available_formats(); 66 return NISSY_ERROR_INVALID_FORMAT; 67 } 68 69 STATIC void 70 log_available_formats(void) 71 { 72 int i; 73 74 LOG("Available formats: "); 75 for (i = 0; ioformat[i].read != NULL; i++) 76 LOG("'%s' ", ioformat[i].name); 77 LOG("\n"); 78 } 79 80 STATIC uint8_t 81 readco(const char *str) 82 { 83 if (*str == '0') 84 return 0; 85 if (*str == '1') 86 return CTWIST_CW; 87 if (*str == '2') 88 return CTWIST_CCW; 89 90 LOG("Error reading CO\n"); 91 return UINT8_ERROR; 92 } 93 94 STATIC uint8_t 95 readcp(const char *str) 96 { 97 uint8_t c; 98 99 for (c = 0; c < 8; c++) 100 if (!strncmp(str, cornerstr[c], 3) || 101 !strncmp(str, cornerstralt[c], 3)) 102 return c; 103 104 LOG("Error reading CP\n"); 105 return UINT8_ERROR; 106 } 107 108 STATIC uint8_t 109 readeo(const char *str) 110 { 111 if (*str == '0') 112 return 0; 113 if (*str == '1') 114 return EFLIP; 115 116 LOG("Error reading EO\n"); 117 return UINT8_ERROR; 118 } 119 120 STATIC uint8_t 121 readep(const char *str) 122 { 123 uint8_t e; 124 125 for (e = 0; e < 12; e++) 126 if (!strncmp(str, edgestr[e], 2)) 127 return e; 128 129 LOG("Error reading EP\n"); 130 return UINT8_ERROR; 131 } 132 133 STATIC cube_t 134 readcube_B32(const char *buf) 135 { 136 int i; 137 uint8_t c[8], e[12]; 138 139 for (i = 0; i < 8; i++) { 140 c[i] = b32tocorner(buf[i]); 141 if (c[i] == UINT8_ERROR) { 142 LOG("Error reading B32 corner %d ", i); 143 if (buf[i] == 0) { 144 LOG("(string terminated early)\n"); 145 } else { 146 LOG("(char '%c')\n", buf[i]); 147 } 148 return ZERO_CUBE; 149 } 150 } 151 152 if (buf[8] != '=') { 153 LOG("Error reading B32 separator: a single '=' " 154 "must be used to separate edges and corners\n"); 155 return ZERO_CUBE; 156 } 157 158 for (i = 0; i < 12; i++) { 159 e[i] = b32toedge(buf[i+9]); 160 if (e[i] == UINT8_ERROR) { 161 LOG("Error reading B32 edge %d ", i); 162 if (buf[i+9] == 0) { 163 LOG("(string terminated early)\n"); 164 } else { 165 LOG("(char '%c')\n", buf[i+9]); 166 } 167 return ZERO_CUBE; 168 } 169 } 170 171 return cubefromarray(c, e); 172 } 173 174 STATIC cube_t 175 readcube_H48(const char *buf) 176 { 177 int i; 178 uint8_t piece, orient, c[8], e[12]; 179 const char *b; 180 181 b = buf; 182 183 for (i = 0; i < 12; i++) { 184 while (*b == ' ' || *b == '\t' || *b == '\n') 185 b++; 186 if ((piece = readep(b)) == UINT8_ERROR) 187 return ZERO_CUBE; 188 b += 2; 189 if ((orient = readeo(b)) == UINT8_ERROR) 190 return ZERO_CUBE; 191 b++; 192 e[i] = piece | orient; 193 } 194 for (i = 0; i < 8; i++) { 195 while (*b == ' ' || *b == '\t' || *b == '\n') 196 b++; 197 if ((piece = readcp(b)) == UINT8_ERROR) 198 return ZERO_CUBE; 199 b += 3; 200 if ((orient = readco(b)) == UINT8_ERROR) 201 return ZERO_CUBE; 202 b++; 203 c[i] = piece | orient; 204 } 205 206 return cubefromarray(c, e); 207 } 208 209 STATIC uint8_t 210 readpiece_LST(const char **b) 211 { 212 uint8_t ret; 213 bool read; 214 215 while (**b == ',' || **b == ' ' || **b == '\t' || **b == '\n') 216 (*b)++; 217 218 for (ret = 0, read = false; **b >= '0' && **b <= '9'; (*b)++) { 219 read = true; 220 ret = ret * 10 + (**b) - '0'; 221 } 222 223 return read ? ret : UINT8_ERROR; 224 } 225 226 STATIC cube_t 227 readcube_LST(const char *buf) 228 { 229 int i; 230 uint8_t c[8], e[12]; 231 232 for (i = 0; i < 8; i++) 233 c[i] = readpiece_LST(&buf); 234 235 for (i = 0; i < 12; i++) 236 e[i] = readpiece_LST(&buf); 237 238 return cubefromarray(c, e); 239 } 240 241 STATIC int64_t 242 writepiece_LST(uint8_t piece, size_t buf_size, char buf[buf_size]) 243 { 244 char digits[3]; 245 size_t i, len; 246 247 if (piece > 99 || buf_size < 3) 248 return 0; 249 250 len = 0; 251 while (piece != 0) { 252 digits[len++] = (piece % 10) + '0'; 253 piece /= 10; 254 } 255 256 if (buf_size < len+2) 257 return 0; 258 259 if (len == 0) 260 digits[len++] = '0'; 261 262 for (i = 0; i < len; i++) 263 buf[i] = digits[len-i-1]; 264 265 buf[len] = ','; 266 buf[len+1] = ' '; 267 268 return len+2; 269 } 270 271 STATIC int64_t 272 writecube_B32(cube_t cube, size_t buf_size, char buf[buf_size]) 273 { 274 int i; 275 uint8_t corner[8], edge[12]; 276 277 if (buf_size < NISSY_SIZE_B32) { 278 LOG("Cannot write cube in B32 format: buffer size must be at " 279 "least %u bytes, but the provided one is %zu bytes.\n", 280 NISSY_SIZE_B32, buf_size); 281 return NISSY_ERROR_BUFFER_SIZE; 282 } 283 284 pieces(&cube, corner, edge); 285 286 for (i = 0; i < 8; i++) 287 buf[i] = cornertob32(corner[i]); 288 289 buf[8] = '='; 290 291 for (i = 0; i < 12; i++) 292 buf[i+9] = edgetob32(edge[i]); 293 294 buf[21] = '\0'; 295 296 return NISSY_OK; 297 } 298 299 STATIC int64_t 300 writecube_H48(cube_t cube, size_t buf_size, char buf[buf_size]) 301 { 302 uint8_t piece, perm, orient, corner[8], edge[12]; 303 int i; 304 305 if (buf_size < NISSY_SIZE_H48) { 306 LOG("Cannot write cube in H48 format: buffer size must be " 307 "at least %u bytes, but the provided one is %zu bytes.\n", 308 NISSY_SIZE_H48, buf_size); 309 return NISSY_ERROR_BUFFER_SIZE; 310 } 311 312 pieces(&cube, corner, edge); 313 314 for (i = 0; i < 12; i++) { 315 piece = edge[i]; 316 perm = piece & PBITS; 317 orient = (piece & EOBIT) >> EOSHIFT; 318 buf[4*i ] = edgestr[perm][0]; 319 buf[4*i + 1] = edgestr[perm][1]; 320 buf[4*i + 2] = orient + '0'; 321 buf[4*i + 3] = ' '; 322 } 323 for (i = 0; i < 8; i++) { 324 piece = corner[i]; 325 perm = piece & PBITS; 326 orient = (piece & COBITS) >> COSHIFT; 327 buf[48 + 5*i ] = cornerstr[perm][0]; 328 buf[48 + 5*i + 1] = cornerstr[perm][1]; 329 buf[48 + 5*i + 2] = cornerstr[perm][2]; 330 buf[48 + 5*i + 3] = orient + '0'; 331 buf[48 + 5*i + 4] = ' '; 332 } 333 334 buf[48+39] = '\0'; 335 336 return NISSY_OK; 337 } 338 339 STATIC int64_t 340 writecube_LST(cube_t cube, size_t buf_size, char buf[buf_size]) 341 { 342 int i; 343 uint64_t ptr; 344 uint8_t piece, corner[8], edge[12]; 345 346 ptr = 0; 347 pieces(&cube, corner, edge); 348 349 for (i = 0; i < 8; i++) { 350 piece = corner[i]; 351 ptr += writepiece_LST(piece, buf_size - ptr, buf + ptr); 352 if (ptr == 0) 353 goto writecube_LST_error; 354 } 355 356 for (i = 0; i < 12; i++) { 357 piece = edge[i]; 358 ptr += writepiece_LST(piece, buf_size - ptr, buf + ptr); 359 if (ptr == 0) 360 goto writecube_LST_error; 361 } 362 363 *(buf+ptr-2) = '\0'; 364 365 return NISSY_OK; 366 367 writecube_LST_error: 368 LOG("Cannot write cube in LST: buffer is too small (%" PRIu64 369 " bytes given). The LST format has a variable size, try a " 370 "larger buffer.\n", buf_size); 371 return NISSY_ERROR_BUFFER_SIZE; 372 } 373 374 STATIC uint8_t 375 b32toedge(char c) 376 { 377 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'f'))) 378 return UINT8_ERROR; 379 380 return c <= 'Z' ? (uint8_t)(c - 'A') : (uint8_t)(c - 'a') + 26; 381 } 382 383 STATIC uint8_t 384 b32tocorner(char c) { 385 uint8_t val; 386 387 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'f'))) 388 return UINT8_ERROR; 389 390 val = c <= 'Z' ? (uint8_t)(c - 'A') : (uint8_t)(c - 'a') + 26; 391 392 return (val & 7) | ((val & 24) << 2); 393 } 394 395 STATIC char 396 edgetob32(uint8_t edge) 397 { 398 return edge < 26 ? 'A' + (char)edge : 'a' + (char)(edge - 26); 399 } 400 401 STATIC char 402 cornertob32(uint8_t corner) 403 { 404 uint8_t val; 405 406 val = (corner & 7) | ((corner & 96) >> 2); 407 408 return val < 26 ? 'A' + (char)val : 'a' + (char)(val - 26); 409 }