Wed, 05 Aug 2009 17:32:05 +0100
updated README
philpem@5 | 1 | /**************************************************************************** |
philpem@5 | 2 | * ptdecode: P-touch PT-2450DX output decoder |
philpem@5 | 3 | ****************************************************************************/ |
philpem@5 | 4 | |
philpem@5 | 5 | #include <cstdio> |
philpem@5 | 6 | #include <exception> |
philpem@5 | 7 | #include "CImg.h" |
philpem@5 | 8 | |
philpem@5 | 9 | using namespace std; |
philpem@5 | 10 | using namespace cimg_library; |
philpem@5 | 11 | |
philpem@5 | 12 | // maximum size of a Ptouch printer head in dots |
philpem@5 | 13 | const unsigned int PT_HEAD_WIDTH = 1024; |
philpem@5 | 14 | |
philpem@5 | 15 | // If defined, makes "blank row" blocks visible |
philpem@5 | 16 | //#define MAKE_BLANK_ROWS_VISIBLE |
philpem@5 | 17 | |
philpem@5 | 18 | // custom exception class for file read errors |
philpem@5 | 19 | class EReadError : public exception { |
philpem@5 | 20 | public: |
philpem@5 | 21 | virtual const char* what() const throw() |
philpem@5 | 22 | { |
philpem@5 | 23 | return "Read error"; |
philpem@5 | 24 | } |
philpem@5 | 25 | }; |
philpem@5 | 26 | |
philpem@5 | 27 | FILE *fp; |
philpem@5 | 28 | |
philpem@5 | 29 | // get next character from file |
philpem@5 | 30 | unsigned char getNext() { |
philpem@5 | 31 | unsigned char ch; |
philpem@5 | 32 | int i; |
philpem@5 | 33 | |
philpem@5 | 34 | i = fread(&ch, 1, 1, fp); |
philpem@5 | 35 | if (i != 1) { |
philpem@5 | 36 | throw EReadError(); |
philpem@5 | 37 | } else { |
philpem@5 | 38 | return ch; |
philpem@5 | 39 | } |
philpem@5 | 40 | } |
philpem@5 | 41 | |
philpem@5 | 42 | // Handler for graphics transfer mode 1 |
philpem@5 | 43 | void runGraphicsXferMode1() |
philpem@5 | 44 | { |
philpem@5 | 45 | bool exit = false; |
philpem@5 | 46 | unsigned int cm = -1; |
philpem@5 | 47 | unsigned long xpos = 0; |
philpem@5 | 48 | unsigned long ypos = 0; |
philpem@5 | 49 | unsigned long ydim = 128; |
philpem@5 | 50 | CImg<unsigned char> img(0, 0, 0, 0, (unsigned char)0); |
philpem@5 | 51 | |
philpem@5 | 52 | while (!exit) { |
philpem@5 | 53 | unsigned char ch = getNext(); |
philpem@5 | 54 | unsigned int len = 0; |
philpem@5 | 55 | unsigned int rowpos = 0; |
philpem@5 | 56 | unsigned char row[PT_HEAD_WIDTH / 8]; // stores uncompressed row data |
philpem@5 | 57 | |
philpem@5 | 58 | switch (ch) { |
philpem@5 | 59 | case 'M': // Set compression mode |
philpem@5 | 60 | ch = getNext(); |
philpem@5 | 61 | cm = ch; |
philpem@5 | 62 | printf("Set compression mode: 0x%02X", ch); |
philpem@5 | 63 | switch (cm) { |
philpem@5 | 64 | case 0x02: |
philpem@5 | 65 | printf(" (TIFF/Packbits)\n"); |
philpem@5 | 66 | break; |
philpem@5 | 67 | default: |
philpem@5 | 68 | printf(" *** Unknown, assuming uncompressed ***\n"); |
philpem@5 | 69 | cm = 1; |
philpem@5 | 70 | break; |
philpem@5 | 71 | } |
philpem@5 | 72 | break; |
philpem@5 | 73 | |
philpem@5 | 74 | case 'Z': // Blank raster line |
philpem@5 | 75 | // Increment x-position and resize the image |
philpem@5 | 76 | img.resize(xpos+1, ydim, 1, 1, 0, 0); |
philpem@5 | 77 | |
philpem@5 | 78 | // Blank the new row |
philpem@5 | 79 | if (img.dimy() > 0) { |
philpem@5 | 80 | // printf("Clear row: x=%lu\n", xpos); |
philpem@5 | 81 | for (int i=0; i<img.dimy(); i++) { |
philpem@5 | 82 | #ifdef MAKE_BLANK_ROWS_VISIBLE |
philpem@5 | 83 | img(xpos, i) = 128; |
philpem@5 | 84 | #else |
philpem@5 | 85 | img(xpos, i) = 255; |
philpem@5 | 86 | #endif |
philpem@5 | 87 | } |
philpem@5 | 88 | } |
philpem@5 | 89 | |
philpem@5 | 90 | xpos++; |
philpem@5 | 91 | break; |
philpem@5 | 92 | |
philpem@5 | 93 | case 'G': // Graphics data row |
philpem@5 | 94 | // decode the length |
philpem@5 | 95 | ch = getNext(); |
philpem@5 | 96 | len = (((int)getNext()) << 8) + ch; |
philpem@5 | 97 | |
philpem@13 | 98 | // Is gfx payload compressed or uncompressed? |
philpem@13 | 99 | if (cm == 1) { |
philpem@13 | 100 | // Uncompressed. Read straight into the row buffer. |
philpem@13 | 101 | while (len > 0) { |
philpem@13 | 102 | row[rowpos++] = getNext(); len--; |
philpem@13 | 103 | } |
philpem@13 | 104 | } else { |
philpem@13 | 105 | // Decompress the gfx data |
philpem@13 | 106 | rowpos = 0; |
philpem@13 | 107 | while (len > 0) { |
philpem@13 | 108 | // get the prefix byte |
philpem@13 | 109 | ch = getNext(); len--; |
philpem@5 | 110 | |
philpem@13 | 111 | // Is this a "run" (a single byte replicated) or a "copy"? |
philpem@13 | 112 | int runlen; |
philpem@13 | 113 | if (ch & 0x80) { |
philpem@13 | 114 | // MSB set, it's a run |
philpem@13 | 115 | runlen = 257 - ((int)ch); |
philpem@5 | 116 | |
philpem@13 | 117 | // Get the byte to replicate, and replicate it into the o/p buffer |
philpem@13 | 118 | ch = getNext(); len--; |
philpem@13 | 119 | while (runlen-- > 0) { |
philpem@13 | 120 | row[rowpos++] = ch; |
philpem@13 | 121 | } |
philpem@13 | 122 | } else { |
philpem@13 | 123 | // MSB clear, it's a copy |
philpem@13 | 124 | runlen = ((int)ch) + 1; |
philpem@13 | 125 | |
philpem@13 | 126 | // Copy N bytes from the input stream to the output |
philpem@13 | 127 | while (runlen-- > 0) { |
philpem@13 | 128 | row[rowpos++] = getNext(); |
philpem@13 | 129 | len--; |
philpem@13 | 130 | } |
philpem@5 | 131 | } |
philpem@5 | 132 | } |
philpem@5 | 133 | } |
philpem@5 | 134 | |
philpem@5 | 135 | // Row decode complete. row contains the image data, and rowpos |
philpem@5 | 136 | // contains its length in bytes. Now shuffle it into CImg... |
philpem@5 | 137 | |
philpem@5 | 138 | // If image height is less than size of image row, then make the |
philpem@5 | 139 | // image taller. |
philpem@5 | 140 | if (((unsigned int)img.dimy()) < (rowpos * 8)) { |
philpem@5 | 141 | ydim = rowpos * 8; |
philpem@5 | 142 | } else { |
philpem@5 | 143 | ydim = img.dimy(); |
philpem@5 | 144 | } |
philpem@5 | 145 | |
philpem@5 | 146 | // Perform the Y resize if necessary, but also make Xdim=Xdim+1 |
philpem@5 | 147 | img.resize(xpos+1, ydim, 1, 1, 0, 0); |
philpem@5 | 148 | |
philpem@5 | 149 | img(xpos, ydim/2) = 128; |
philpem@5 | 150 | |
philpem@5 | 151 | // Now copy the image data... |
philpem@5 | 152 | ypos = 0; |
philpem@5 | 153 | for (unsigned int byte=0; byte<rowpos; byte++) { |
philpem@5 | 154 | for (unsigned int bit=0; bit<8; bit++) { |
philpem@5 | 155 | if (row[byte] & (0x80>>bit)) { |
philpem@5 | 156 | img(xpos, ypos) = 0; |
philpem@5 | 157 | } else { |
philpem@5 | 158 | img(xpos, ypos) = 255; |
philpem@5 | 159 | } |
philpem@5 | 160 | |
philpem@5 | 161 | // Increment y-position |
philpem@5 | 162 | ypos++; |
philpem@5 | 163 | } |
philpem@5 | 164 | } |
philpem@5 | 165 | |
philpem@5 | 166 | // An entire row has been decoded. Increment x-position. |
philpem@5 | 167 | xpos++; |
philpem@5 | 168 | break; |
philpem@5 | 169 | |
philpem@5 | 170 | case 0x0c: // FF |
philpem@5 | 171 | printf("Formfeed: Print without label feed (job completed, more labels follow)\n"); |
philpem@5 | 172 | exit = true; |
philpem@5 | 173 | break; |
philpem@5 | 174 | |
philpem@5 | 175 | case 0x1a: // Ctrl-Z |
philpem@5 | 176 | printf("Ctrl-Z: Print with label feed (job completed, no further labels)\n"); |
philpem@5 | 177 | exit = true; |
philpem@5 | 178 | break; |
philpem@5 | 179 | |
philpem@5 | 180 | default: // Something else |
philpem@5 | 181 | printf("** Unrecognised command prefix in gfx mode: 0x%02x\n", ch); |
philpem@5 | 182 | break; |
philpem@5 | 183 | } |
philpem@5 | 184 | } |
philpem@5 | 185 | |
philpem@5 | 186 | // Display the contents of the image |
philpem@5 | 187 | img.display(); |
philpem@5 | 188 | } |
philpem@5 | 189 | |
philpem@5 | 190 | // Parse an ESC i command |
philpem@5 | 191 | void parse_esc_i() |
philpem@5 | 192 | { |
philpem@5 | 193 | unsigned char ch = getNext(); |
philpem@5 | 194 | unsigned int tmpI; |
philpem@5 | 195 | |
philpem@5 | 196 | switch (ch) { |
philpem@5 | 197 | case 'B': // ESC i B: Specify baud rate |
philpem@5 | 198 | tmpI = getNext(); |
philpem@5 | 199 | ch = getNext(); |
philpem@5 | 200 | tmpI += ((int)ch)*256; |
philpem@5 | 201 | printf("Set baud rate:\t%d00", tmpI); |
philpem@5 | 202 | if ((tmpI != 96) && (tmpI != 576) && (tmpI != 1152)) { |
philpem@5 | 203 | printf(" [ILLEGAL SETTING]\n"); |
philpem@5 | 204 | } else { |
philpem@5 | 205 | printf("\n"); |
philpem@5 | 206 | } |
philpem@5 | 207 | break; |
philpem@5 | 208 | |
philpem@5 | 209 | case 'S': // ESC i S: Status request |
philpem@5 | 210 | printf("Printer status request\n"); |
philpem@5 | 211 | break; |
philpem@5 | 212 | |
philpem@5 | 213 | case 'M': // ESC i M: Set mode |
philpem@5 | 214 | ch = getNext(); |
philpem@5 | 215 | printf("Set mode 0x%02X:\tAutoCut %s, Mirror %s\n", ch, |
philpem@5 | 216 | (ch & 0x40) ? "on" : "off", |
philpem@5 | 217 | (ch & 0x80) ? "on" : "off"); |
philpem@5 | 218 | break; |
philpem@5 | 219 | |
philpem@5 | 220 | case 'd': // ESC i d: Set margin amount (feed amount) |
philpem@5 | 221 | tmpI = getNext(); |
philpem@5 | 222 | ch = getNext(); |
philpem@5 | 223 | tmpI += ((int)ch)*256; |
philpem@5 | 224 | printf("Set margin:\t%d dots", tmpI); |
philpem@5 | 225 | break; |
philpem@5 | 226 | |
philpem@5 | 227 | case 'K': // ESC i K: Set expanded mode |
philpem@5 | 228 | ch = getNext(); |
philpem@5 | 229 | printf("Set expanded mode 0x%02X:\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n", ch, |
philpem@5 | 230 | (ch & 0x04) ? "Half-cut on" : "Half-cut off", |
philpem@5 | 231 | (ch & 0x08) ? "Chain-print off: last label will be fed and cut" : "Chain-print on: last label will NOT be fed or cut", |
philpem@5 | 232 | (ch & 0x20) ? "Label end cut: when printing multiple copies, end of last label is cut" : "Label end cut off", |
philpem@5 | 233 | (ch & 0x40) ? "High-resolution (360x720dpi)" : "Normal resolution (360x360dpi)", |
philpem@5 | 234 | (ch & 0x80) ? "Copy-printing on (expansion buffer not cleared on form-feed)" : "Copy-printing off" |
philpem@5 | 235 | ); |
philpem@5 | 236 | break; |
philpem@5 | 237 | |
philpem@5 | 238 | case 'R': // ESC i R: Set graphics transfer mode |
philpem@5 | 239 | ch = getNext(); |
philpem@5 | 240 | printf("Set graphics transfer mode 0x%02X: ", ch); |
philpem@5 | 241 | if (ch == 1) { |
philpem@5 | 242 | printf("Raster graphics mode\n"); |
philpem@5 | 243 | runGraphicsXferMode1(); |
philpem@5 | 244 | } else { |
philpem@5 | 245 | printf("\n\tUnrecognised graphics transfer mode: remainder of data may be garbage.\n"); |
philpem@5 | 246 | } |
philpem@5 | 247 | break; |
philpem@5 | 248 | |
philpem@5 | 249 | default: |
philpem@5 | 250 | printf("Unrecognised cmnd: ESC i 0x%02X\n", ch); |
philpem@5 | 251 | break; |
philpem@5 | 252 | } |
philpem@5 | 253 | } |
philpem@5 | 254 | |
philpem@5 | 255 | // Parse an ESC command |
philpem@5 | 256 | void parse_esc() |
philpem@5 | 257 | { |
philpem@5 | 258 | unsigned char ch = getNext(); |
philpem@5 | 259 | |
philpem@5 | 260 | switch(ch) { |
philpem@5 | 261 | case 'i': // ESC i: Brother-specific extensions |
philpem@5 | 262 | parse_esc_i(); |
philpem@5 | 263 | break; |
philpem@5 | 264 | |
philpem@5 | 265 | case '@': // ESC @: Initialize |
philpem@5 | 266 | printf("Initialize: clear buffer and reset print origin\n"); |
philpem@5 | 267 | break; |
philpem@5 | 268 | |
philpem@5 | 269 | default: |
philpem@5 | 270 | printf("Unrecognised cmnd: ESC 0x%02X\n", ch); |
philpem@5 | 271 | break; |
philpem@5 | 272 | } |
philpem@5 | 273 | } |
philpem@5 | 274 | |
philpem@5 | 275 | int main(int argc, char **argv) |
philpem@5 | 276 | { |
philpem@5 | 277 | // check params |
philpem@5 | 278 | if (argc != 2) { |
philpem@5 | 279 | // wrong! |
philpem@5 | 280 | printf("Usage: %s filename\n", argv[0]); |
philpem@5 | 281 | return -1; |
philpem@5 | 282 | } |
philpem@5 | 283 | |
philpem@5 | 284 | // open binary dump file |
philpem@5 | 285 | fp = fopen(argv[1], "rb"); |
philpem@5 | 286 | if (!fp) { |
philpem@5 | 287 | printf("Error opening source file\n"); |
philpem@5 | 288 | return -1; |
philpem@5 | 289 | } |
philpem@5 | 290 | |
philpem@5 | 291 | try { |
philpem@5 | 292 | while (true) { |
philpem@5 | 293 | unsigned char ch; |
philpem@5 | 294 | |
philpem@5 | 295 | ch = getNext(); |
philpem@5 | 296 | |
philpem@5 | 297 | switch (ch) { |
philpem@5 | 298 | case 0x00: // NULL |
philpem@5 | 299 | printf("Null\n"); |
philpem@5 | 300 | break; |
philpem@5 | 301 | case 0x0c: // FF |
philpem@5 | 302 | printf("Formfeed: Print without feed\n"); |
philpem@5 | 303 | break; |
philpem@5 | 304 | case 0x1a: // Ctrl-Z |
philpem@5 | 305 | printf("Ctrl-Z: Print with label feed\n"); |
philpem@5 | 306 | break; |
philpem@5 | 307 | case 0x1b: // ESC |
philpem@5 | 308 | parse_esc(); |
philpem@5 | 309 | break; |
philpem@5 | 310 | default: |
philpem@5 | 311 | printf("Unrecognised cmnd: 0x%02X\n", ch); |
philpem@5 | 312 | break; |
philpem@5 | 313 | } |
philpem@5 | 314 | } |
philpem@5 | 315 | } catch (EReadError &e) { |
philpem@5 | 316 | if (feof(fp)) { |
philpem@5 | 317 | printf("EOF reached.\n"); |
philpem@5 | 318 | } else { |
philpem@5 | 319 | printf("Uncaught EReadException: %s\n", e.what()); |
philpem@5 | 320 | } |
philpem@5 | 321 | } catch (exception &e) { |
philpem@5 | 322 | printf("Uncaught exception: %s\n", e.what()); |
philpem@5 | 323 | } catch (...) { |
philpem@5 | 324 | printf("Uncaught and unrecognised exception. Something went *really* wrong here...\n"); |
philpem@5 | 325 | } |
philpem@5 | 326 | |
philpem@5 | 327 | // close the file |
philpem@5 | 328 | fclose(fp); |
philpem@5 | 329 | |
philpem@5 | 330 | return 0; |
philpem@5 | 331 | } |