Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Stream.cpp

Go to the documentation of this file.
00001 /*
00002  * Stream.cpp
00003  *
00004  * Part of Fly! Legacy project
00005  *
00006  * Copyright 2003 Chris Wallace
00007  *
00008  * Fly! Legacy is free software; you can redistribute it and/or modify
00009  *   it under the terms of the GNU General Public License as published by
00010  *   the Free Software Foundation; either version 2 of the License, or
00011  *   (at your option) any later version.
00012  *
00013  * Fly! Legacy is distributed in the hope that it will be useful,
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *   GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  *   along with Fly! Legacy; if not, write to the Free Software
00020  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00047 #include "../Include/Globals.h"
00048 #include "../Include/FlyLegacy.h"
00049 #include "../Include/Utility.h"
00050 
00051 
00052 //
00053 // Local function that reads the next line from the stream file, discards
00054 //   comment lines, and returns a pointer to the start of the first
00055 //   non-whitespace character in the line.  The complete line is copied
00056 //   in to the buffer pointed to by s.
00057 //
00058 static char*  sgets (char* s, int maxLength, SStream *stream)
00059 {
00060   char* p = s;
00061 
00062   bool keepGoing = true;
00063   while (keepGoing) {
00064     pgets (s, maxLength, (PODFILE*)stream->stream);
00065 
00066     // Skip whitespace and comments
00067     p = s;
00068     while ((*p == ' ') || (*p == '\t')) p++;
00069 
00070     // If not a comment, then go ahead and parse it
00071     if (strncmp (p, "//", 2) != 0) keepGoing = false;
00072   }
00073 
00074   return p;
00075 }
00076 
00077 
00078 static Tag    snexttag (char* s, int maxLength, SStream *stream)
00079 {
00080   Tag tag = 0;
00081 
00082   char *p;
00083   bool gotTag = false;
00084   while (!gotTag) {
00085     p = sgets (s, maxLength, stream);
00086     // Check for brackets
00087     if ((strlen(p) >= 6) && (p[0] == '<') && (p[5] == '>')) {
00088       char tagstring[5];
00089       strncpy (tagstring, (p+1), 4);
00090       tag = StringToTag (tagstring);
00091       gotTag = true;
00092     }
00093   }
00094   return tag;
00095 }
00096 
00097 
00098 //
00099 // OpenStream - Open a stream file for reading
00100 //
00101 // The function returns 1 if the stream was successfully opened, 0 otherwise
00102 //
00103 
00105 
00106 int   OpenStream(SStream *stream)
00107 {
00108   return OpenStream (&globals->pfs, stream);
00109 }
00110 
00111 
00112 //
00113 // OpenStream - Open a stream file for reading
00114 //
00115 // The function returns 1 if the stream was successfully opened, 0 otherwise
00116 //
00117 
00119 
00120 int   OpenStream(PFS *pfs, SStream *stream)
00121 {
00122   int rc = 0;
00123 
00124   // Only allow read-access for now
00125   if (strcmp (stream->mode, "r") == 0) {
00126     // Open PODFILE
00127     stream->stream = popen (pfs, stream->filename);
00128 
00129     if (stream->stream != NULL) {
00130       // OK
00131       rc = 1;
00132     }
00133   }
00134 
00135   return rc;
00136 }
00137 
00138 
00139 //
00140 // CloseStream - Close a stream file
00141 //
00142 // This function closes the specified stream file.  No further read/write operations
00143 //   can be performed on the SStream object.
00144 //
00145 void  CloseStream(SStream *stream)
00146 {
00147   pclose ((PODFILE*)stream->stream);
00148   stream->stream = NULL;
00149 }
00150 
00151 
00152 //
00153 // ReadFrom - Initialize a CStreamObject from the contents of a stream file
00154 //
00155 // Example (CAircraft is descended from CStreamObject):
00156 //   CAircraft *pAircraft = new CAircraft();
00157 //   SStream *pStream = new SStream;
00158 //   strcpy (pStream->filename, "World\\Flyhawk.nfo");
00159 //   strcpy (pStream->mode, "r");
00160 //   if (OpenStream (pStream)) {
00161 //     // Stream successfully opened
00162 //     ReadFrom (pAircraft, pStream);
00163 //   }
00164 //
00165 void  ReadFrom(CStreamObject *object, SStream *stream)
00166 {
00167   // The first tag should be <bgno>.  Some default files (e.g. TRI40.PNL) have
00168   //   missing <bgno> tags so this code must be tolerant of that.
00169   char s[256];
00170   Tag tag;
00171   bool openingBgno = true;
00172 
00173   // Start sending tags to the CStreamObject, up until (and not including)
00174   //   the <endo> corresponding to the <bgno> found above.  A count of
00175   //   nested sub-objects is kept to ensure that we don't quit prematurely
00176   int nestCount = 0;
00177 
00178   // Now continue to read tags until <endo>, sending them to CStreamObject::Read
00179   while ( ! (((tag = snexttag (s, 256, stream)) == 'endo') && (nestCount == 0))) {
00180     if (tag == 'bgno') {
00181       if (openingBgno) {
00182         // This is the expected opening <bgno>, just ignore it
00183       } else {
00184         // This is the start of a nested sub-object, increment nestCount
00185         nestCount++;
00186         object->Read (stream, tag);
00187       }
00188     } else if (tag == 'endo') {
00189       // This is the end of a nested sub-object, so decrement nestCount.
00190       //   Note that if nestCount is already zero, then we wouldn't get here
00191       nestCount--;
00192       object->Read (stream, tag);
00193     } else {
00194       // Normal tag, pass it to the object and keep going
00195       object->Read (stream, tag);
00196     }
00197 
00198     // After the first tag has been read, we are no longer expecting an
00199     //   opening <bgno>
00200     openingBgno = false;
00201   }
00202 
00203   // Finally call CStreamObject::ReadFinished
00204   object->ReadFinished();
00205 }
00206 
00207 
00208 //
00209 // SkipObject - Skip over an object bracketed by <bgno> <endo> tags
00210 //
00211 // This function is called when it is desired to skip over a sub-object
00212 //   (and all of its nested sub-sub-objects) in a stream file.  The first
00213 //   tag read should be <bgno> which marks the beginning of the object to
00214 //   be skipped.  From this point on, all tags and values are read and
00215 //   discarded until the <endo> tag corresponding to the beginning tag
00216 //   is read.
00217 //
00218 void  SkipObject(SStream *stream)
00219 {
00220   // Advance to <bgno> tag
00221   char s[256];
00222   Tag tag;
00223   while ((tag = snexttag (s, 256, stream)) != 'bgno') {}
00224 
00225   // Continue reading tags up to the <endo> corresponding to the <bgno> found above.
00226   //   A count ofnested sub-objects is kept to ensure that we don't quit prematurely
00227   int nestCount = 0;
00228 
00229   // Now continue to read tags until <endo>, sending them to CStreamObject::Read
00230   bool keepGoing = true;
00231   while (keepGoing) {
00232     tag = snexttag (s, 256, stream);
00233     // == 'endo') && (nestCount == 0))) {
00234     if (tag == 'bgno') {
00235       // This is the start of a nested sub-object, so increment nestCount
00236       nestCount++;
00237     } else if (tag == 'endo') {
00238       // This is the end of either a nested sub-object (if nestCount > 0) or
00239       //   the end of the object we're skipping (if nestCount == 0)
00240       if (nestCount > 0) {
00241         nestCount--;
00242       } else {
00243         keepGoing = false;
00244       }
00245     }
00246   }
00247 }
00248 
00249 
00250 //
00251 // AdvanceToTag - Skip forward until the specified tag is found
00252 //
00253 
00254 void  AdvanceToTag(unsigned int tag, SStream *stream)
00255 {
00256 
00257 }
00258 
00259 
00260 
00261 //
00262 // ReadInt - Read a (signed) integer value from the stream
00263 //
00264 
00265 void  ReadInt (int *value, SStream *stream)
00266 {
00267   // Read up to next token
00268   char s[256];
00269   char *p = sgets (s, 256, stream);
00270   *value = atoi (p);
00271 }
00272 
00273 
00274 //
00275 // ReadUInt - Read an unsigned integer value from the stream
00276 //
00277 
00278 void  ReadUInt (unsigned int *value, SStream *stream)
00279 {
00280   // Read up to next token
00281   char s[256];
00282   char *p = sgets (s, 256, stream);
00283 
00284   // Parse data value
00285   *value = (unsigned int)(atoi(p));
00286 }
00287 
00288 
00289 //
00290 // ReadFloat - Read a single-precision float value value from the stream
00291 //
00292 void  ReadFloat (float *value, SStream *stream)
00293 {
00294   // Read up to next token
00295   char s[256];
00296   ReadString (s, 256, stream);
00297   *value = (float)(atof (s));
00298 }
00299 
00300 
00301 //
00302 // ReadDouble - Read a double-precision float value value from the stream
00303 //
00304 void  ReadDouble (double *value, SStream *stream)
00305 {
00306   // Read up to next token
00307   char s[256];
00308   ReadString (s, 256, stream);
00309   *value = atof (s);
00310 }
00311 
00312 
00313 //
00314 // ReadString - Read a newline-terminated character string from the stream
00315 //
00316 void  ReadString (char *value, int maxLength, SStream *stream)
00317 {
00318   // Read up to next token
00319   char s[256];
00320   char *p = sgets (s, 256, stream);
00321 
00322   // Trim all trailing whitespace by walking backwards from end of string until
00323   //   a non-whitespace char is found, then replace the succeeding char with \0
00324   char *q = p + strlen(p) - 1;
00325   while (isspace(*q)) q--;
00326   q++;
00327   *q = '\0';
00328 
00329   if ((int)strlen(p) >= maxLength) {
00330     // String was longer than supplied buffer
00331     globals->logWarning->Write ("ReadString : Line truncated (len=%d max=%d)\n%s",
00332       strlen(p), maxLength, p);
00333     strncpy (value, p, maxLength-1);
00334   } else {
00335     strcpy (value, p);
00336   }
00337 }
00338 
00339 
00340 //
00341 // ReadVector - Read a three-component single-precision float vecotr from the stream
00342 //
00343 void  ReadVector (SVector *value, SStream *stream)
00344 {
00345   // Read up to next token
00346   char s[256];
00347   char *p = sgets (s, 256, stream);
00348 
00349   // Parse the data value
00350   float x, y, z;
00351   sscanf (p, "%f, %f, %f", &x, &y, &z);
00352   value->x = (double)x;
00353   value->y = (double)y;
00354   value->z = (double)z;
00355 }
00356 
00357 //
00358 // ReadLatLon - Read a geographical latitude/longitude position from a stream
00359 //
00360 // Format is:
00361 //    33 56 24.6 N          (N 33 deg 56 min 24.6 sec latitude)
00362 //    118 23 56.5 W         (W 118 deg, 23 min, 56.5 sec longitude)
00363 //
00364 void  ReadLatLon (SPosition *value, SStream *stream)
00365 {
00366   // Declare locals for parsing of position strings
00367   char s[256];
00368   int deg, min;
00369   float sec;
00370   char  hemi;
00371 
00372   // First line is latitude
00373   ReadString (s, 256, stream);
00374   double lat = 0;
00375   if (sscanf (s, "%d %d %f %c", &deg, &min, &sec, &hemi) == 4) {
00376     switch (hemi) {
00377     case 'N':
00378     case 'n':
00379       lat = (deg * 3600.0) + (min * 60.0) + sec;
00380       break;
00381 
00382     case 'S':
00383     case 's':
00384       lat = -((deg * 3600.0) + (min * 60.0) + sec);
00385       break;
00386 
00387     default:
00388       gtfo ("ReadLatLon : Invalid latitude hemisphere <%c>", hemi);
00389     }
00390   } else {
00391     gtfo ("ReadLatLon : Malformed latitude string <%s>", s);
00392   }
00393   value->lat = lat;
00394 
00395   // Second line is longitude
00396   ReadString (s, 256, stream);
00397   double lon = 0;
00398   if (sscanf (s, "%d %d %f %c", &deg, &min, &sec, &hemi) == 4) {
00399     switch (hemi) {
00400     case 'E':
00401     case 'e':
00402       lon = (deg * 3600.0) + (min * 60.0) + sec;
00403       break;
00404 
00405     case 'W':
00406     case 'w':
00407       lon = (360.0 * 3600.0) - ((deg * 3600.0) + (min * 60.0) + sec);
00408       break;
00409 
00410     default:
00411       gtfo ("ReadPosition : Invalid longitude hemisphere <%c>", hemi);
00412     }
00413   } else {
00414     gtfo ("ReadPosition : Malformed longitude string <%s>", s);
00415   }
00416   value->lon = lon;
00417 
00418   // Set altitude to zero
00419   value->alt = 0;
00420 }
00421 
00422 
00423 //
00424 // ReadPosition - Read a geographical position from the stream
00425 //
00426 // Format is:
00427 //    33 56'24.6405"N        (N 33 deg 56 min 24.6405 sec latitude)
00428 //    118 23'56.5173"W       (W 118 deg, 23 min, 56.5173 sec longitude)
00429 //    94.248616728049114     (94 feet above MSL altitude)
00430 //
00431 void  ReadPosition (SPosition *value, SStream *stream)
00432 {
00433   // Declare locals for parsing of position strings
00434   char s[256];
00435   int deg, min;
00436   float sec;
00437   char  hemi;
00438 
00439   // First line is latitude
00440   ReadString (s, 256, stream);
00441   double lat = 0;
00442   if (sscanf (s, "%d %d'%f\"%c", &deg, &min, &sec, &hemi) == 4) {
00443     switch (hemi) {
00444     case 'N':
00445     case 'n':
00446       lat = (deg * 3600.0) + (min * 60.0) + sec;
00447       break;
00448 
00449     case 'S':
00450     case 's':
00451       lat = -((deg * 3600.0) + (min * 60.0) + sec);
00452       break;
00453 
00454     default:
00455       gtfo ("ReadPosition : Invalid latitude hemisphere <%c>", hemi);
00456     }
00457   } else {
00458     gtfo ("ReadPosition : Malformed latitude string <%s>", s);
00459   }
00460   value->lat = lat;
00461 
00462   // Second line is longitude
00463   ReadString (s, 256, stream);
00464   double lon = 0;
00465   if (sscanf (s, "%d %d'%f\"%c", &deg, &min, &sec, &hemi) == 4) {
00466     switch (hemi) {
00467     case 'E':
00468     case 'e':
00469       lon = (deg * 3600.0) + (min * 60.0) + sec;
00470       break;
00471 
00472     case 'W':
00473     case 'w':
00474       lon = (360.0 * 3600.0) - ((deg * 3600.0) + (min * 60.0) + sec);
00475       break;
00476 
00477     default:
00478       gtfo ("ReadPosition : Invalid longitude hemisphere <%c>", hemi);
00479     }
00480   } else {
00481     gtfo ("ReadPosition : Malformed longitude string <%s>", s);
00482   }
00483   value->lon = lon;
00484 
00485   // Third line is altitude
00486   ReadDouble (&value->alt, stream);
00487 }
00488 
00489 
00490 //
00491 // ReadTime - Read a date/time value from the stream
00492 //
00493 void  ReadTime (SDateTime *value, SStream *stream)
00494 {
00495 
00496 }
00497 
00498 
00499 //
00500 // ReadTimeDelta - Read a time delta value from the stream
00501 //
00502 void  ReadTimeDelta (SDateTimeDelta *value, SStream *stream)
00503 {
00504 
00505 }
00506 
00507 
00508 //
00509 // ReadMessage - Read a message object from a stream
00510 //
00511 void  ReadMessage (SMessage *msg, SStream *stream)
00512 {
00513   // Advance to <bgno> tag
00514   char s[256];
00515   Tag tag;
00516 
00517   // The following kluge is in place because of a typo in a <mesg> object
00518   //   in the default panel TRI30.PNL which misspells <bgno> as <bgmo>
00519   while (((tag = snexttag (s, 256, stream)) != 'bgno') && (tag != 'bgmo')) {}
00520 
00521   // Now continue to read tags until <endo>, populating various fields of the
00522   //   SMessage struct
00523   bool keepGoing = true;
00524   while (keepGoing) {
00525     tag = snexttag (s, 256, stream);
00526     switch (tag) {
00527     case 'grou':
00528     case 'conn':
00529       // Group (unique ID) tag
00530       {
00531         Tag grou;
00532         ReadTag (&grou, stream);
00533         msg->group = grou;
00534       }
00535       break;
00536 
00537     case 'user':
00538     case 'usr1':
00539       // Various members of the user union
00540       {
00541         char s[64];
00542         ReadString (s, 64, stream);
00543 
00544         char dtag_string[16];
00545         char group_string[16];
00546         if (sscanf (s, "DATATAG,'%s'", dtag_string) == 1) {
00547           msg->user.u.datatag = StringToTag (dtag_string);
00548         } else if (sscanf (s, "GROUP,'%s'", group_string) == 1) {
00549           msg->group = StringToTag (group_string);
00550         }
00551       }
00552       break;
00553 
00554     case 'dtag':
00555       // Datatag member of user data
00556       {
00557         Tag dtag;
00558         ReadTag (&dtag, stream);
00559         msg->user.u.datatag = dtag;
00560       }
00561       break;
00562 
00563     case 'type':
00564       // Data type and initial value
00565       {
00566         char s[64];
00567         ReadString (s, 64, stream);
00568 
00570       }
00571       break;
00572 
00573     case 'endo':
00574       // End of message
00575       keepGoing = false;
00576       break;
00577 
00578     default:
00579       // Unrecognized tag
00580       char s[64];
00581       TagToString (s, tag);
00582       globals->logWarning->Write ("ReadMessage : Unrecognized tag %s", s);
00583     }
00584   }
00585 }
00586 
00587 
00588 //
00589 // ReadTag - Read a four-character datatag value from the stream
00590 //
00591 void  ReadTag (Tag *tag, SStream *stream)
00592 {
00593   char s[64];
00594   ReadString (s, 64, stream);
00595 
00596   // Pad string with spaces to four chars in length
00597   while (strlen(s) < 4) {
00598     strcat (s, " ");
00599   }
00600 
00601   *tag = StringToTag (s);
00602 }
SourceForge.net Logo Documentation generated by doxygen