//dst.cpp // .DST (Tajima) embroidery file read/write routines // Format comments are thanks to tspilman@dalcoathletic.com who's // notes appeared at http://www.wotsit.org under Tajima Format. #include "stdafx.h" #include #include #include #include #include "dst.h" #include "helpers.h" #include "Messaging.h" extern CEmbroidermodderApp theApp; // Procedures int getbit(unsigned char b, int pos) { int bit; bit = (b>>pos)&1; return (bit); }; int decode_record_dx (char b0, char b1, char b2) { int x=0; x+= getbit(b2,2)*(+81); x+= getbit(b2,3)*(-81); x+= getbit(b1,2)*(+27); x+= getbit(b1,3)*(-27); x+= getbit(b0,2)*(+9); x+= getbit(b0,3)*(-9); x+= getbit(b1,0)*(+3); x+= getbit(b1,1)*(-3); x+= getbit(b0,0)*(+1); x+= getbit(b0,1)*(-1); return(x); }; int decode_record_dy (char b0, char b1, char b2) { int y=0; y+= getbit(b2,5)*(+81); y+= getbit(b2,4)*(-81); y+= getbit(b1,5)*(+27); y+= getbit(b1,4)*(-27); y+= getbit(b0,5)*(+9); y+= getbit(b0,4)*(-9); y+= getbit(b1,7)*(+3); y+= getbit(b1,6)*(-3); y+= getbit(b0,7)*(+1); y+= getbit(b0,6)*(-1); return(y); }; int decode_record_flags (unsigned char b2) { if (b2==243) return(END); switch (b2&195) { case 3: return(NORMAL); case 131: return(JUMP); case 195: return(STOP); default: return(UNKNOWN); }; }; unsigned char setbit(int pos) { return(1<+121 or < -121. if (x>=+41) {b2+=setbit(2); x-=81;}; if (x<=-41) {b2+=setbit(3); x+=81;}; if (x>=+14) {b1+=setbit(2); x-=27;}; if (x<=-14) {b1+=setbit(3); x+=27;}; if (x>=+ 5) {b0+=setbit(2); x-= 9;}; if (x<=- 5) {b0+=setbit(3); x+= 9;}; if (x>=+ 2) {b1+=setbit(0); x-= 3;}; if (x<=- 2) {b1+=setbit(1); x+= 3;}; if (x>=+ 1) {b0+=setbit(0); x-= 1;}; if (x<=- 1) {b0+=setbit(1); x+= 1;}; if(x!=0) { //error ASSERT(x==0); }; if (y>=+41) {b2+=setbit(5); y-=81;}; if (y<=-41) {b2+=setbit(4); y+=81;}; if (y>=+14) {b1+=setbit(5); y-=27;}; if (y<=-14) {b1+=setbit(4); y+=27;}; if (y>=+ 5) {b0+=setbit(5); y-= 9;}; if (y<=- 5) {b0+=setbit(4); y+= 9;}; if (y>=+ 2) {b1+=setbit(7); y-= 3;}; if (y<=- 2) {b1+=setbit(6); y+= 3;}; if (y>=+ 1) {b0+=setbit(7); y-= 1;}; if (y<=- 1) {b0+=setbit(6); y+= 1;}; if(y!=0) { //error ASSERT(y==0); }; switch (flags) { case NORMAL: b2+=(char)3; break; case END: b2=(char)243; b0=b1=(char)0; break; case JUMP: b2+=(char)131; break; case STOP: b2+=(char)195; break; default: b2+=3; //fprintf(fout,"Unknown\n"); break; }; b[0]=b0;b[1]=b1;b[2]=b2; }; int atorgb(char *val) { // expect string beginning with 6 hex digits int i; int rgb; rgb=0; for (i=0;i<6;i++) { if (val[i]>='0'&&val[i]<='9') { rgb=rgb*16+val[i]-'0'; } else if (val[i]>='A'&&val[i]<='F') { rgb=rgb*16+val[i]-'A'; } else if (val[i]>='a'&&val[i]<='f') { rgb=rgb*16+val[i]-'a'; } else if (val[i]==','||val[i]=='\0') { // early delimiter break; } else { // unknown character break; }; }; return(rgb); }; //convert 2 characters into 1 int for case statement //#define cci(s) (s[0]*256+s[1]) #define cci(c1,c2) (c1*256+c2) void set_dst_variable(class pattern *pattern, char *var, char *val) { unsigned int i; for(i=0;i<=strlen(var);i++) { // upcase var if(var[i]>='a'&&var[i]<='z') var[i]+='A'-'a'; } // macro converts 2 characters to 1 int, allows case statement... switch(cci(var[0],var[1])) { case cci('L','A'): //Design Name (LA) pattern->set_variable("Design_Name",val); break; case cci('S','T'): //Stitch count, 7 digits padded by leading 0's case cci('C','O'): //Color change count, 3 digits padded by leading 0's case cci('+','X'): //Design extents (+/-X,+/-Y), 5 digits padded by leading 0's case cci('-','X'): case cci('+','Y'): case cci('-','Y'): // don't store these variables, they are recalculated at save break; case cci('A','X'): //Relative coordinates of last point, 6 digits, padded with leading spaces, first char may be +/- case cci('A','Y'): case cci('M','X'): //Coordinates of last point in previous file of multi-volume design, 6 digits, padded with leading spaces, first char may be +/- case cci('M','Y'): // store these variables as-is, they will be converted to numbers and back at save; pattern->set_variable(var,val); break; case cci('P','D'): // store this string as-is, it will be saved as-is, 6 characters if (strlen(val)!=6) { pattern->messages.add("Warning: in DST file read, PD is not 6 characters, but ",(int)strlen(val)); }; pattern->set_variable(var,val); break; // Begin extended fields section case cci('A','U'): //Author string, arbitrary length case cci('C','P'): //Copyright string, arbitrary length pattern->set_variable(var,val); break; case cci('T','C'): //Thread Color: #RRGGBB,Description,Catalog Number (1st field RGB hex values, 2nd&3rd fields optional arbitrary length) //rgb=atorgb(val); //description=split_cell_str(val,2); //catalog_number=split_cell_str(val,3); pattern->add_color(atorgb(val),split_cell_str(val,2),split_cell_str(val,3)); break; default: //unknown field, just save it. pattern->set_variable(var,val); break; }; }; BOOL dst_read(class pattern *pattern, const char *filename) { char header[512+1];// The header seems to contain information about the design. // Seems to be ASCII text delimited by 0x0D (carriage returns). // This must be in the file for most new software or hardware // to consider it a good file! This is much more important // than I originally believed. The header is 125 bytes in // length and padded out by 0x20 to 512 bytes total. // All entries in the header seem to be 2 ASCII characters // followed by a colon, then it’s value trailed by a carriage // return. // char LA[16+1]; // First is the ‘LA’ entry, which is the design name with no // path or extension information. The blank is 16 characters // in total, but the name must not be longer that 8 characters // and padded out with 0x20. // char ST[7+1]; // Next is the stitch count ST, this is a 7 digit number // padded by leading zeros. This is the total stitch count // including color changes, jumps, nups, and special records. // char CO[3+1]; // Next, is CO or colors, a 3 digit number padded by leading // zeros. This is the number of color change records in the // file. // char POSX[5+1]; // Next is +X or the positive X extent in centimeters, a 5 // digit non-decimal number padded by leading zeros. // char NEGX[5+1]; // Following is the -X or the negative X extent in millimeters, // a 5 digit non-decimal number padded by leading zeros. // char POSY[5+1]; // Again, the +Y extents. // char NEGY[5+1]; // Again, the -Y extents. // char AX[6+1]; // AX and AY should express the relative coordinates of the // char AY[6+1]; // last point from the start point in 0.1 mm. If the start // and last points are the same, the coordinates are (0,0). // char MX[6+1]; // MX and MY should express coordinates of the last point of // char MY[6+1]; // the previous file for a multi-volume design. A multi- // volume design means a design consisted of two or more files. // This was used for huge designs that can not be stored in a // single paper tape roll. It is not used so much (almost // never) nowadays. // char PD[9+1]; // PD is also storing some information for multi-volume design. FILE *dstin; int i; // for converting stitches from file encoding unsigned char b0,b1,b2; int dx,dy; int flags; pattern->clear(); pattern->set_variable("file_name",filename); dstin = fopen(filename,"r"); if (dstin==0) { theApp.status.set("Error opening DST file for read:",filename); pattern->messages.add("Error opening DST file for read:",filename); return (FALSE); }; _setmode( _fileno( dstin ), _O_BINARY ); // READ 512 BYTE HEADER INTO header[] for (i=0;i<512;i++) { header[i]=fgetc(dstin); }; // TODO:It would probably be a good idea to validate file before accepting it. char var[3]; // temporary storage variable name char val[512]; // temporary storage variable value int valpos; // fill variables from header fields for (i=0;i<512;i++) { if (header[i]==':') { var[0]=header[i-2]; var[1]=header[i-1]; var[2]='\0'; valpos=i+1; for (i++;i<512;i++) { // don't accept : without CR because there's a bug below: i-valpos must be > 0 which is not the case if the : is before the third character. if(header[i]==13/*||header[i]==':'*/) { // 0x0d = carriage return if(header[i]==':') { // : indicates another variable, CR was missing! i-=2; }; strncpy(val,&header[valpos],i-valpos); val[i-valpos]='\0'; set_dst_variable(pattern,var,val); //pattern->set_variable(var,val); pattern->messages.add(var,val); break; }; }; }; }; pattern->messages.add("Begin reading stitches:", pattern->get_variable("st")); int co=1; // count number of colors //READ STITCH RECORDS for (i=0;!feof(dstin);i++) { b0=fgetc(dstin); if (feof(dstin)) break; b1=fgetc(dstin); if (feof(dstin)) break; b2=fgetc(dstin); if (feof(dstin)) break; dx=decode_record_dx(b0,b1,b2); dy=decode_record_dy(b0,b1,b2); flags=decode_record_flags(b2); //file stores distance in 0.1mm, stitches stored internally in pattern are mm. pattern->add_stitch_rel(double(dx)/10.0,double(dy)/10.0,flags); if(flags==STOP) co++; }; fclose(dstin); //TODO: Another good idea would be to verify header information here (dimensions, colors, etc) // then warn if there is a discrepancy. pattern->messages.add("Done reading stitches:",pattern->stitches); #ifdef DEBUG_FILEOUT fprintf(fout,"Done reading: %d stitches\n",pattern->stitches); fclose(fout); #endif //assign random colors srand((unsigned)time(NULL)); for(;pattern->colorsadd_color(RGB(rand()%256,rand()%256,rand()%256),"Random",""); }; return(TRUE); }; BOOL dst_write(class pattern *pattern, const char *filename) { FILE *dstout; int xx,yy,dx,dy,flags; int negx,posx,negy,posy,st,co; int i; unsigned char b[3]; int inserts=0; double fxx,fyy,fdx,fdy; // char buf[1000]; //temp storage for debug output // char buf2[1000]; if (pattern->stitches==0) { theApp.status.set("No file to save."); fprintf(stderr,"No file to save\n"); return(FALSE); }; dstout = fopen(filename,"w"); if (dstout==0) { theApp.status.set("Error opening DST file for write:",filename); return(FALSE); }; _setmode( _fileno( dstout ), _O_BINARY ); //first pass through pattern calculating extents and fixing overlength xx=yy=0; co=1; st=0; posx=negx=posy=negy=0; flags=NORMAL; for (i=1;flags!=END;i++) { if (i>pattern->stitches) break; xx=round(pattern->stitchlist[i-1].xx * 10.0); // convert from mm to 0.1mm for file format yy=round(pattern->stitchlist[i-1].yy * 10.0); dx=round(pattern->stitchlist[i].xx*10.0) - xx; dy=round(pattern->stitchlist[i].yy*10.0) - yy; // insert jump point if out of range. if (abs(dx)>=121 || abs(dy)>=121) { fxx=pattern->stitchlist[i-1].xx; // use doubles to do insert calculation fyy=pattern->stitchlist[i-1].yy; fdx=pattern->stitchlist[i].xx - fxx; fdy=pattern->stitchlist[i].yy - fyy; int splits; // number of stitches to split overlength one into if (fabs(fdx)>fabs(fdy)) { splits = round(ceil(fabs(fdx)/12.10)); } else { splits = round(ceil(fabs(fdy)/12.10)); }; //sprintf(buf,"Split adds %d between %lf,%lf (#%d) and %lf,%lf (#%d); dx=%lf,dy=%lf:\n", // splits-1, // pattern->stitchlist[i-1].xx,pattern->stitchlist[i-1].yy, i-1, // pattern->stitchlist[i].xx,pattern->stitchlist[i].yy, i, // fdx,fdy); //theApp.status.set(buf); theApp.status.set("DST format requires spliting long stitch #",i); //pattern->messages.add(buf); for (int j=1;jadd_stitch_abs(fxx+fdx*j/(splits),fyy+fdy*j/(splits),JUMP); //insert these jump stitches before i pattern->move_last_stitch(i+j-1); inserts++; //sprintf(buf2,"added jump stitch %d: xx=%lf, yy=%lf\n",i+j-1,pattern->stitchlist[i+j-1].xx,pattern->stitchlist[i+j-1].yy); //pattern->messages.add(buf2); }; }; xx=round(pattern->stitchlist[i].xx * 10.0); // convert from mm to 0.1mm for file format yy=round(pattern->stitchlist[i].yy * 10.0); flags=pattern->stitchlist[i].flags; if (xxposx) posx=xx; if (yyposy) posy=yy; st++; if (flags==STOP) co++; }; if (inserts>0) { pattern->messages.add("DST format limitation (max dx,dy=121) required adding jump stitches to design: ",inserts); }; char *la = stralloccopy(pattern->get_variable("design_name")); if (strlen(la)>16) la[16]='\0'; //write header fprintf(dstout,"LA:%-16s\x0d",la); free (la); fprintf(dstout,"ST:%7d\x0d",st); fprintf(dstout,"CO:%3d\x0d",co-1); // number of color changes, not number of colors! fprintf(dstout,"+X:%5d\x0d",posx); fprintf(dstout,"-X:%5d\x0d",abs(negx)); fprintf(dstout,"+Y:%5d\x0d",posy); fprintf(dstout,"-Y:%5d\x0d",abs(negy)); int ax,ay,mx,my; char *pd; ax=ay=mx=my=0; ax=pattern->get_variable_int("ax"); // will return 0 if not defined ay=pattern->get_variable_int("ay"); mx=pattern->get_variable_int("mx"); my=pattern->get_variable_int("my"); pd=pattern->get_variable("pd"); // will return null pointer if not defined if (pd==0 || strlen(pd)!=6) { // pd is not valid, so fill in a default consisting of ****** pd="******"; }; fprintf(dstout,"AX:+%5d\x0d",ax); fprintf(dstout,"AY:+%5d\x0d",ay); fprintf(dstout,"MX:+%5d\x0d",mx); fprintf(dstout,"MY:+%5d\x0d",my); fprintf(dstout,"PD:%6s\x0d",pd); fprintf(dstout,"\x1a"); // 0x1a is the code for end of section. // pad out header to proper length for(i=125;i<512;i++) { fprintf(dstout," "); }; //write stitches xx=yy=0; for (i=0;istitches;i++) { //convert from mm to 0.1mm for file format dx=round(pattern->stitchlist[i].xx*10.0) - xx; dy=round(pattern->stitchlist[i].yy*10.0) - yy; xx=round(pattern->stitchlist[i].xx*10.0); yy=round(pattern->stitchlist[i].yy*10.0); flags=pattern->stitchlist[i].flags; encode_record(b,dx,dy,flags); fprintf(dstout,"%c%c%c",b[0],b[1],b[2]); }; fprintf(dstout,"\x1a"); // finish file with a terminator character //close file fclose(dstout); return(TRUE); };