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 2021-02-18
18 
19  Copyright (c) 2021 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 #include <errno.h>
30 
31 #define OLD_HEADER_SIZE 12
32 #define NEW_HEADER_SIZE 100
33 #define MIN_RECORD_SIZE 3 // type+0-length+FF
34 
36 
37 int main(int argc, char** argv) {
38  fprintf(stderr, "\"%s\", version 2021-02-18. Copyright (c) 2021 Live Networks, Inc. All rights reserved.\n", argv[0]);
39  fprintf(stderr, "For the latest version of this program (and more information), visit http://djilogs.live555.com\n");
40 
41  int fileNamePos = 1;
42  if (argc != 2) {
43  fprintf(stderr, "Usage: %s <txtFileName>\n", argv[0]);
44  return 1;
45  }
46  char const* fileName = argv[fileNamePos];
47 
48  FILE* fid = fopen(fileName, "rb");
49  if (fid == NULL) {
50  fprintf(stderr, "Failed to open \"%s\"\n", fileName);
51  return 1;
52  }
53 
54  // Figure out the file's size
55  struct stat sb;
56  u_int64_t fileSize;
57  if (stat(fileName, &sb) == 0) {
58  fileSize = sb.st_size;
59  } else {
60  fprintf(stderr, "Failed to get file size\n");
61  return 1;
62  }
63 
64  // Check the file size:
65  if (fileSize < OLD_HEADER_SIZE) {
66  fprintf(stderr, "Bad file size: %llu bytes\n", fileSize);
67  return 1;
68  }
69 
70  // Map the file into memory:
71  u_int8_t* const mappedFile
72  = (u_int8_t*)mmap(0, fileSize, PROT_READ, MAP_SHARED, fileno(fid), 0);
73  if (mappedFile == MAP_FAILED) {
74  extern int errno;
75  fprintf(stderr, "mmap() call failed: %s\n", strerror(errno));
76  return 1;
77  }
78 
79  // Figure out the start and end position of the header/records/details areas:
80  u_int64_t recordsAreaStart, recordsAreaEnd;
81  u_int64_t detailsAreaStart, detailsAreaEnd;
82 
83  // The first 8 bytes (little-endian) of the file points to the end of records area:
84  u_int8_t const* ptr = mappedFile;
85  recordsAreaEnd = getWord64LE(ptr);
86 
87  // The next 2 bytes (little-endian) is the size of the details area:
88  u_int16_t const detailsAreaSize = get2BytesLE(ptr);
89 
90  // The next byte is the version number of the file:
91  fileVersionNumber = *ptr++;
92  fprintf(stderr, "File version number: 0x%02x\n", fileVersionNumber);
93 
94  // The next byte is unknown; skip over it:
95  ++ptr;
96 
97  // Old versions of the file have a smaller header, and are not scrambled:
98  unsigned headerSize;
99  int isScrambled;
100  if (fileVersionNumber < 0x06) {
101  headerSize = OLD_HEADER_SIZE;
102  isScrambled = 0;
103  } else {
104  headerSize = NEW_HEADER_SIZE;
105  isScrambled = 1;
106  }
107 
108  // Newer versions of the file have a different order:
109  u_int64_t computedFileSize;
110  if (fileVersionNumber >= 0x0C) {
111  // Order is "header";"details area";"records area"
112  recordsAreaStart = headerSize + detailsAreaSize;
113  detailsAreaStart = headerSize;
114  detailsAreaEnd = detailsAreaStart + detailsAreaSize;
115 
116  computedFileSize = recordsAreaEnd;
117  } else {
118  // Order is "header";"records area";"details area"
119  recordsAreaStart = headerSize;
120  detailsAreaStart = recordsAreaEnd;
121  detailsAreaEnd = detailsAreaStart + detailsAreaSize;
122 
123  computedFileSize = detailsAreaEnd;
124  }
125 
126  // Check the sanity of these values:
127  u_int64_t const minFileSize = headerSize + MIN_RECORD_SIZE;
128  if (computedFileSize < minFileSize || computedFileSize > fileSize) {
129  fprintf(stderr, "Bad 'recordsAreaEnd' %llu (0x%llx) and/or 'detailsAreaSize' %u (0x%x); file size is %llu\n",
130  recordsAreaEnd, recordsAreaEnd,
131  detailsAreaSize, detailsAreaSize,
132  fileSize);
133  return 1;
134  }
135 
136  // Create a parser:
138 
139  // Begin by parsing the 'DETAILS' area:
140  ptr = &mappedFile[detailsAreaStart];
141  parser->parseDetailsArea(ptr, &mappedFile[detailsAreaEnd]);
142 
143  // Then, parse all of the records in the file:
144  ptr = &mappedFile[recordsAreaStart];
145  u_int8_t* const recordsAreaEndPtr = &mappedFile[recordsAreaEnd];
146 
147  while (ptr < recordsAreaEndPtr) {
148 #ifdef DEBUG_RECORD_PARSING
149  u_int64_t curFilePosition = ptr - mappedFile;
150  fprintf(stderr, "@0x%08llx: ", curFilePosition);
151 #endif
152  if (!parser->parseRecord(ptr, recordsAreaEndPtr, isScrambled)) break;
153  }
154  if (ptr < recordsAreaEndPtr) {
155  u_int64_t curFilePosition = ptr - mappedFile;
156  fprintf(stderr, "Premature end of record parsing at file position %llu (0x%08llx)\n", curFilePosition, curFilePosition);
157  }
158  parser->outputOneRow(); // the final row of data
159  parser->summarizeRecordParsing();
160 
161  delete parser;
162 
163  fprintf(stderr, "Done writing CSV.\n");
164  return 0;
165 }
#define OLD_HEADER_SIZE
Definition: djiparsetxt.cpp:31
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:32
int main(int argc, char **argv)
Definition: djiparsetxt.cpp:37
virtual void outputOneRow(int outputColumnLabels=0)=0
u_int8_t fileVersionNumber
Definition: djiparsetxt.cpp:35
#define MIN_RECORD_SIZE
Definition: djiparsetxt.cpp:33
virtual void parseDetailsArea(u_int8_t const *&ptr, u_int8_t const *limit)=0
u_int16_t get2BytesLE(u_int8_t const *&ptr, u_int8_t const *limit)
virtual int parseRecord(u_int8_t const *&ptr, u_int8_t const *limit, int isScrambled)=0