#include "crane.h" #include #define READ_BUF_SIZE 1024 #define MAX_LINE 2048 static wchar_t abuff[MAX_LINE]; extern FEATURE_KIND gakind[]; extern size_t gasize[]; wchar_t *LastLineSample() { return abuff; } BOOL AllocFeature(SAMPLE *, int); void PutElement(FILE *fpo, void *pv, int type, wchar_t chSep) { int status; switch (type) { case typeBOOL: status = fwprintf(fpo, L"%c%c", chSep, *((BYTE *) pv) ? L'T' : L'F'); ASSERT(status == 2); break; case typeBYTE: status = fwprintf(fpo, L"%c%02X", chSep, *((BYTE *) pv)); ASSERT(status == 3); break; case type8DOT8: status = fwprintf(fpo, L"%c%04X", chSep, *((WORD *) pv)); ASSERT(status == 5); break; case typeSHORT: status = fwprintf(fpo, L"%c%d", chSep, *((SHORT *) pv)); ASSERT(status >= 2 && status <= 7); break; case typeUSHORT: status = fwprintf(fpo, L"%c%d", chSep, *((USHORT *) pv)); ASSERT(status >= 2 && status <= 6); break; case typePOINTS: status = fwprintf(fpo, L"%c%d,%d", chSep, ((END_POINTS *) pv)->start, ((END_POINTS *) pv)->end); ASSERT(status >= 4 && status <= 14); break; case typeDRECTS: status = fwprintf(fpo, L"%c%d,%d,%d,%d", chSep, ((DRECTS *) pv)->x, ((DRECTS *) pv)->y, ((DRECTS *) pv)->w, ((DRECTS *) pv)->h); ASSERT(status >= 8 && status <= 28); break; case typeRECTS: status = fwprintf(fpo, L"%c%d,%d,%d,%d", chSep, ((RECTS *) pv)->x1, ((RECTS *) pv)->y1, ((RECTS *) pv)->x2, ((RECTS *) pv)->y2); ASSERT(status >= 8 && status <= 28); break; default: break; } } // Write out one featurized ink sample BOOL WriteSample(SAMPLE *_this, FILE *fpo) { int ifeat; int ielem; int cstrk; int size; int type; BYTE *base; // Get the stroke count cstrk = _this->cstrk; if ( fwprintf(fpo, L"%02d %04X <%s %d %d>", cstrk, _this->wchLabel, _this->aSampleFile, _this->ipanel, _this->ichar) < 15 || fwprintf(fpo, L" %d %d,%d,%d,%d", _this->fDakuten, _this->drcs.x, _this->drcs.y, _this->drcs.w, _this->drcs.h) < 10 ) { return FALSE; } for (ielem = 0; ielem < MAX_RECOG_ALTS; ielem++) { if ( fwprintf(fpo, L"%c%04X", ielem ? L',' : L' ', _this->awchAlts[ielem]) < 5 ) { return FALSE; } } for (ifeat = 0; ifeat < FEATURE_COUNT; ifeat++) { // Get the number of elements in this feature switch (gakind[ifeat].freq) { case freqSTROKE: base = (BYTE *) (_this->apfeat[ifeat]->data); type = gakind[ifeat].type; size = gasize[type]; for (ielem = 0; ielem < cstrk; ielem++) PutElement(fpo, (void *) (base + ielem * size), type, (wchar_t) (ielem ? L':' : L' ')); break; case freqFEATURE: case freqSTEP: case freqPOINT: break; } // Print the element list } if ( fwprintf(fpo, L"\n") != 1 ) { return FALSE; } return TRUE; } // Read a hexadecimal number wchar_t *GetHEXADEC(wchar_t *pbuff, long *pval) { wchar_t *pRef = pbuff; *pval = 0; while (iswxdigit(*pbuff)) { *pval <<= 4; *pval += (BYTE)(*pbuff < L'A' ? *pbuff - L'0' : *pbuff - L'A' + 10); ++pbuff; } ASSERT(pRef + 1 <= pbuff); ASSERT(pRef + 8 >= pbuff); return pbuff; } // Read in a signed integer wchar_t *GetInteger(wchar_t *pbuff, long *pval) { wchar_t *pRef = pbuff; BOOL bNeg = FALSE; if (*pbuff == L'-') { pbuff++; bNeg = TRUE; } *pval = 0; while (iswdigit(*pbuff)) { *pval *= 10; *pval += (BYTE)(*pbuff - L'0'); ++pbuff; } if (bNeg) *pval = -(*pval); ASSERT(pRef + 1 + bNeg <= pbuff); ASSERT(pRef + 11 >= pbuff); return pbuff; } wchar_t *GetPOINT(wchar_t *pbuff, POINT *ppt) { if (((pbuff = GetInteger(pbuff, &ppt->x)) == (wchar_t *) NULL) || (*pbuff++ != L',')) { ASSERT(FALSE); return (wchar_t *) NULL; } return GetInteger(pbuff, &ppt->y); } wchar_t *GetRECT(wchar_t *pbuff, RECT *prc) { if (((pbuff = GetInteger(pbuff, &prc->left)) == (wchar_t *) NULL) || (*pbuff++ != L',')) { ASSERT(FALSE); return (wchar_t *) NULL; } if (((pbuff = GetInteger(pbuff, &prc->top)) == (wchar_t *) NULL) || (*pbuff++ != L',')) { ASSERT(FALSE); return (wchar_t *) NULL; } if (((pbuff = GetInteger(pbuff, &prc->right)) == (wchar_t *) NULL) || (*pbuff++ != L',')) { ASSERT(FALSE); return (wchar_t *) NULL; } return GetInteger(pbuff, &prc->bottom); } wchar_t *GetElement(wchar_t *pbuff, void *pv, int type) { long val; POINT pt; RECT rc; switch (type) { case typeBOOL: if (*pbuff == L'T') *((BYTE *) pv) = TRUE, pbuff++; else if (*pbuff == L'F') *((BYTE *) pv) = FALSE, pbuff++; else { ASSERT(FALSE); pbuff = (wchar_t *) NULL; } break; case typeBYTE: pbuff = GetHEXADEC(pbuff, &val); ASSERT(pbuff); *((BYTE *) pv) = (BYTE) val; break; case type8DOT8: pbuff = GetHEXADEC(pbuff, &val); ASSERT(pbuff); *((WORD *) pv) = (WORD) val; break; case typeSHORT: pbuff = GetInteger(pbuff, &val); ASSERT(pbuff); *((short *) pv) = (short) val; break; case typeUSHORT: pbuff = GetInteger(pbuff, &val); ASSERT(pbuff); *((USHORT *) pv) = (USHORT) val; break; case typePOINTS: pbuff = GetPOINT(pbuff, &pt); ASSERT(pbuff); ((END_POINTS *) pv)->start = (short) pt.x; ((END_POINTS *) pv)->end = (short) pt.y; break; case typeDRECTS: pbuff = GetRECT(pbuff, &rc); ASSERT(pbuff); ((DRECTS *) pv)->x = (short) rc.left; ((DRECTS *) pv)->y = (short) rc.top; ((DRECTS *) pv)->w = (short) rc.right; ((DRECTS *) pv)->h = (short) rc.bottom; break; case typeRECTS: pbuff = GetRECT(pbuff, &rc); ASSERT(pbuff); ((RECTS *) pv)->x1 = (short) rc.left; ((RECTS *) pv)->y1 = (short) rc.top; ((RECTS *) pv)->x2 = (short) rc.right; ((RECTS *) pv)->y2 = (short) rc.bottom; break; default: pbuff = (wchar_t *) NULL; ASSERT(pbuff); break; } return pbuff; } // Read in a feature list. This will allocate the sample if needed as well as all the space // needed to store the feature lists. SAMPLE *DoReadSample(SAMPLE *_this) { BOOL bAlloc = FALSE; BOOL bFailed = FALSE; int ifeat; int ielem; int type; int size; BYTE *base; wchar_t *pbuff; unsigned long uLong; int status; if (_this == (SAMPLE *) NULL) { if ((_this = (SAMPLE *) ExternAlloc(sizeof(SAMPLE))) == (SAMPLE *) NULL) { ASSERT(FALSE); return (SAMPLE *) NULL; } bAlloc = TRUE; } InitFeatures(_this); // Get the first items: stroke count, codepoint, file name and file index status = swscanf(abuff, L"%2d %4X <%s %d %d>", &_this->cstrk, &_this->wchLabel, _this->aSampleFile, &_this->ipanel, &_this->ichar); if (status != 5) { return (SAMPLE *) NULL; } // Position the input cursor to the space just after the closing angle bracket of the file info pbuff = abuff; while (*pbuff && (*pbuff++ != L'>')) ; if (pbuff < abuff + 15) { return (SAMPLE *) NULL; } if (pbuff > abuff + 50) { return (SAMPLE *) NULL; } // The dakuten and guide features live directly in the sample, handle them pbuff = GetElement(++pbuff, (void *) &_this->fDakuten, typeSHORT); if (!pbuff) { return (SAMPLE *) NULL; } pbuff = GetElement(++pbuff, (void *) &_this->drcs, typeDRECTS); if (!pbuff) { return (SAMPLE *) NULL; } // The Zilla alternate list comes next ++pbuff; // Skip space pbuff = GetHEXADEC(pbuff, &uLong); if (!pbuff) { return (SAMPLE *) NULL; } _this->awchAlts[0] = (wchar_t)uLong; for (ielem = 1; ielem < MAX_RECOG_ALTS && pbuff && *pbuff++ == L','; ielem++) { pbuff = GetHEXADEC(pbuff, &uLong); if (!pbuff) { return (SAMPLE *) NULL; } _this->awchAlts[ielem] = (wchar_t)uLong; } // Allocate the remaining features and read them. If the cursor is ever pointing // at something we don't expect, panic. for (ifeat = 0; ifeat < FEATURE_COUNT; ifeat++) { if (bFailed || !AllocFeature(_this, ifeat) || *pbuff != L' ') { FreeFeatures(_this); if (bAlloc) ExternFree(_this); ASSERT(FALSE); return (SAMPLE *) NULL; } // OK, now we have space to store the results switch (gakind[ifeat].freq) { case freqSTROKE: base = (BYTE *) (_this->apfeat[ifeat]->data); type = gakind[ifeat].type; size = gasize[type]; for (ielem = 0; ielem < _this->cstrk; ielem++) { // Each element should be preceded by a colon if (ielem && (*pbuff != L':')) bFailed = TRUE; pbuff = GetElement(++pbuff, (void *) (base + ielem * size), type); if (pbuff == (wchar_t *) NULL) { ASSERT(pbuff); bFailed = TRUE; break; } } break; case freqFEATURE: case freqSTEP: case freqPOINT: break; } } if (bFailed) { FreeFeatures(_this); if (bAlloc) ExternFree(_this); ASSERT(FALSE); return (SAMPLE *) NULL; } return _this; } // Read using stdio library. SAMPLE *ReadSample(SAMPLE *_this, FILE *fpi) { if (!fgetws(abuff, MAX_LINE, fpi)) { ASSERT(feof(fpi)); return (SAMPLE *) NULL; } return DoReadSample(_this); } // EOF Flag for DoLineRead used by ReadSampleH static int g_fEOF = FALSE; // Reset EOF Flag for DoLineRead. void ResetReadSampleH() { g_fEOF = FALSE; } // Read one line of input. BOOL DoLineRead(HANDLE hFile, wchar_t *pBuf, int sizeBuf) { static int iReadBuf = 0; static int cReadBuf = 0; static BYTE aReadBuf[READ_BUF_SIZE]; BOOL fHaveCR; int cBuf; // Check for end of file on last call. if (g_fEOF) { return FALSE; } // Make sure we keep room for null. --sizeBuf; // Loop until we have a full line. cBuf = 0; fHaveCR = FALSE; while (TRUE) { DWORD bytesRead; // Make sure we have something in the buffer. if (iReadBuf == cReadBuf) { if (!ReadFile(hFile, aReadBuf, READ_BUF_SIZE, &bytesRead, NULL)) { // Read error! ASSERT(0); return FALSE; } if (bytesRead == 0) { // EOF g_fEOF = TRUE; pBuf[cBuf] = L'\0'; return cBuf != 0; } cReadBuf = bytesRead; iReadBuf = 0; } // If we had a CR last time we must have a LF this time. if (fHaveCR) { if (aReadBuf[iReadBuf] == '\n') { ++iReadBuf; } else { // Missing LF?!?! ASSERT(0); } break; } // Copy one character, checking for end of line. // We convert to Unicode, but since we just use // plain ASCII, so all we do is zero extend. if (aReadBuf[iReadBuf] == '\r') { ++iReadBuf; fHaveCR = TRUE; } else if (aReadBuf[iReadBuf] == '\n') { // Floating NL?!?! ++iReadBuf; break; } else { if (cBuf >= sizeBuf) { // Line too long! ASSERT(0); break; } pBuf[cBuf++] = (wchar_t)aReadBuf[iReadBuf++]; } } // Terminate string and return. pBuf[cBuf] = L'\0'; return TRUE; } // Read using Windows calls. SAMPLE *ReadSampleH(SAMPLE *_this, HANDLE hFile) { // Outer loop to deal with stuped COPY comand putting ^Z characters in the file. do { if (!DoLineRead(hFile, abuff, MAX_LINE)){ return (SAMPLE *)-1; } } while (abuff[0] == L'\x1a' && abuff[1] == L'\0'); return DoReadSample(_this); }