djiparsetxt
djiparsetxt.cpp
Go to the documentation of this file.
1 /**********
2 This program is free software: you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation, either version 3 of the License, or
5 (at your option) any later version.
6 
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11 
12 You should have received a copy of the GNU General Public License
13 along with this program. If not, see <http://www.gnu.org/licenses/>.
14 **********/
15 /*
16  A C++ program to parse DJI's ".txt" log files (recorded by the "DJI Go 4" app).
17  Version 2019-02-08
18 
19  Copyright (c) 2019 Live Networks, Inc. All rights reserved.
20  For the latest version of this program (and more information), visit http://djilogs.live555.com
21 */
22 
23 #include "DJITxtParser.hh"
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/mman.h>
29 
30 #define OLD_HEADER_SIZE 12
31 #define NEW_HEADER_SIZE 100
32 #define MIN_RECORD_SIZE 3 // type+0-length+FF
33 
35 
36 int main(int argc, char** argv) {
37  fprintf(stderr, "\"%s\", version 2019-02-08. Copyright (c) 2019 Live Networks, Inc. All rights reserved.\n", argv[0]);
38  fprintf(stderr, "For the latest version of this program (and more information), visit http://djilogs.live555.com\n");
39 
40  int fileNamePos = 1;
41  if (argc != 2) {
42  fprintf(stderr, "Usage: %s <txtFileName>\n", argv[0]);
43  return 1;
44  }
45  char const* fileName = argv[fileNamePos];
46 
47  FILE* fid = fopen(fileName, "rb");
48  if (fid == NULL) {
49  fprintf(stderr, "Failed to open \"%s\"\n", fileName);
50  return 1;
51  }
52 
53  // Figure out the file's size
54  struct stat sb;
55  u_int64_t fileSize;
56  if (stat(fileName, &sb) == 0) {
57  fileSize = sb.st_size;
58  } else {
59  fprintf(stderr, "Failed to get file size\n");
60  return 1;
61  }
62 
63  // Check the file size:
64  if (fileSize < OLD_HEADER_SIZE) {
65  fprintf(stderr, "Bad file size: %lld bytes\n", fileSize);
66  return 1;
67  }
68 
69  // Map the file into memory:
70  u_int8_t* const mappedFile
71  = (u_int8_t*)mmap(0, fileSize, PROT_READ, MAP_SHARED, fileno(fid), 0);
72  if (mappedFile == MAP_FAILED) {
73  extern int errno;
74  fprintf(stderr, "mmap() call failed: %s\n", strerror(errno));
75  return 1;
76  }
77 
78  // Get/check the first 8 bytes (little-endian) of the file; it's the size of the header+record area:
79  u_int8_t const* ptr = mappedFile;
80  u_int64_t headerPlusRecordAreaSize = getWord64LE(ptr);
81 
82  // The next 4 bytes are the file version number (apparently big-endian):
84  fprintf(stderr, "File version number: 0x%08x\n", fileVersionNumber);
85 
86 
87  // Old versions of the file have a smaller header, and are not scrambled:
88  unsigned headerSize;
89  int isScrambled;
90  if ((fileVersionNumber&0x0000FF00) < 0x00000600) {
91  headerSize = OLD_HEADER_SIZE;
92  isScrambled = 0;
93  } else {
94  headerSize = NEW_HEADER_SIZE;
95  isScrambled = 1;
96  }
97 
98  unsigned const minFileSize = headerSize + MIN_RECORD_SIZE;
99  if (headerPlusRecordAreaSize < minFileSize || headerPlusRecordAreaSize > fileSize) {
100  fprintf(stderr, "Bad 'header+record-area' size: %lld (0x%llx); file size is %lld\n", headerPlusRecordAreaSize, headerPlusRecordAreaSize, fileSize);
101  return 1;
102  }
103 
104  // Create a parser:
106 
107  // Begin by parsing the 'DETAILS' area (the data after the header+record area):
108  u_int8_t* const detailsArea = &mappedFile[headerPlusRecordAreaSize];
109  u_int8_t* const endOfDetailsArea = &mappedFile[fileSize];
110  ptr = detailsArea;
111  parser->parseDetailsArea(ptr, endOfDetailsArea);
112 
113  // Then, parse all of the records in the file:
114  u_int8_t* const recordArea = &mappedFile[headerSize];
115  u_int8_t* const endOfRecordArea = detailsArea;
116 
117  ptr = recordArea;
118  while (ptr < endOfRecordArea) {
119 #ifdef DEBUG_RECORD_PARSING
120  u_int64_t curFilePosition = ptr - mappedFile;
121  fprintf(stderr, "@0x%08llx: ", curFilePosition);
122 #endif
123  if (!parser->parseRecord(ptr, endOfRecordArea, isScrambled)) break;
124  }
125  if (ptr < endOfRecordArea) {
126  u_int64_t curFilePosition = ptr - mappedFile;
127  fprintf(stderr, "Premature end of record parsing at file position %lld (0x%08llx)\n", curFilePosition, curFilePosition);
128  }
129  parser->outputOneRow(); // the final row of data
130  parser->summarizeRecordParsing();
131 
132  delete parser;
133 
134  fprintf(stderr, "Done writing CSV.\n");
135  return 0;
136 }
#define OLD_HEADER_SIZE
Definition: djiparsetxt.cpp:30
static DJITxtParser * createNew()
u_int64_t getWord64LE(u_int8_t const *&ptr, u_int8_t const *limit)
virtual void summarizeRecordParsing()=0
#define NEW_HEADER_SIZE
Definition: djiparsetxt.cpp:31
int main(int argc, char **argv)
Definition: djiparsetxt.cpp:36
u_int32_t fileVersionNumber
Definition: djiparsetxt.cpp:34
virtual void outputOneRow(int outputColumnLabels=0)=0
#define MIN_RECORD_SIZE
Definition: djiparsetxt.cpp:32
virtual void parseDetailsArea(u_int8_t const *&ptr, u_int8_t const *limit)=0
virtual int parseRecord(u_int8_t const *&ptr, u_int8_t const *limit, int isScrambled)=0
unsigned getWord32BE(u_int8_t const *&ptr, u_int8_t const *limit)