1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 hash.cpp
6
7 Function to handle hash functions (checksums)
8
9 Based on original idea by Farfetch'd
10
11 ***************************************************************************/
12
13 #include "hash.h"
14 #include "hashing.h"
15 #include <cctype>
16
17
18 namespace util {
19 //**************************************************************************
20 // GLOBAL VARIABLES
21 //**************************************************************************
22
23 char const hash_collection::HASH_CRC;
24 char const hash_collection::HASH_SHA1;
25
26 char const *const hash_collection::HASH_TYPES_CRC = "R";
27 char const *const hash_collection::HASH_TYPES_CRC_SHA1 = "RS";
28 char const *const hash_collection::HASH_TYPES_ALL = "RS";
29
30 char const hash_collection::FLAG_NO_DUMP;
31 char const hash_collection::FLAG_BAD_DUMP;
32
33
34
35 //**************************************************************************
36 // HASH COLLECTION
37 //**************************************************************************
38
39 //-------------------------------------------------
40 // hash_collection - constructor
41 //-------------------------------------------------
42
hash_collection()43 hash_collection::hash_collection()
44 : m_has_crc32(false),
45 m_has_sha1(false),
46 m_creator(nullptr)
47 {
48 }
49
50
hash_collection(const char * string)51 hash_collection::hash_collection(const char *string)
52 : m_has_crc32(false),
53 m_has_sha1(false),
54 m_creator(nullptr)
55 {
56 from_internal_string(string);
57 }
58
59
hash_collection(const hash_collection & src)60 hash_collection::hash_collection(const hash_collection &src)
61 : m_has_crc32(false),
62 m_has_sha1(false),
63 m_creator(nullptr)
64 {
65 copyfrom(src);
66 }
67
68
69 //-------------------------------------------------
70 // ~hash_collection - destructor
71 //-------------------------------------------------
72
~hash_collection()73 hash_collection::~hash_collection()
74 {
75 delete m_creator;
76 }
77
78
79 //-------------------------------------------------
80 // operator= - assignment operator
81 //-------------------------------------------------
82
operator =(const hash_collection & src)83 hash_collection &hash_collection::operator=(const hash_collection &src)
84 {
85 // ignore self-assignment
86 if (this != &src)
87 copyfrom(src);
88 return *this;
89 }
90
91
92 //-------------------------------------------------
93 // operator== - test for equality
94 //-------------------------------------------------
95
operator ==(const hash_collection & rhs) const96 bool hash_collection::operator==(const hash_collection &rhs) const
97 {
98 // match CRCs
99 int matches = 0;
100 if (m_has_crc32 && rhs.m_has_crc32)
101 {
102 if (m_crc32 != rhs.m_crc32)
103 return false;
104 matches++;
105 }
106
107 // match SHA1s
108 if (m_has_sha1 && rhs.m_has_sha1)
109 {
110 if (m_sha1 != rhs.m_sha1)
111 return false;
112 matches++;
113 }
114
115 // if all shared hashes match, return true
116 return (matches > 0);
117 }
118
119
120 //-------------------------------------------------
121 // hash_types - return a list of hash types as
122 // a string
123 //-------------------------------------------------
124
hash_types() const125 std::string hash_collection::hash_types() const
126 {
127 std::string buffer;
128 if (m_has_crc32)
129 buffer.push_back(HASH_CRC);
130 if (m_has_sha1)
131 buffer.push_back(HASH_SHA1);
132 return buffer;
133 }
134
135
136 //-------------------------------------------------
137 // reset - reset the hash collection to an empty
138 // set of hashes and flags
139 //-------------------------------------------------
140
reset()141 void hash_collection::reset()
142 {
143 m_flags.clear();
144 m_has_crc32 = m_has_sha1 = false;
145 delete m_creator;
146 m_creator = nullptr;
147 }
148
149
150 //-------------------------------------------------
151 // add_from_string - add a new hash, importing
152 // from a string
153 //-------------------------------------------------
154
add_from_string(char type,const char * buffer,int length)155 bool hash_collection::add_from_string(char type, const char *buffer, int length)
156 {
157 // handle CRCs
158 if (type == HASH_CRC)
159 return m_has_crc32 = m_crc32.from_string(buffer, length);
160
161 // handle SHA1s
162 else if (type == HASH_SHA1)
163 return m_has_sha1 = m_sha1.from_string(buffer, length);
164
165 return false;
166 }
167
168
169 //-------------------------------------------------
170 // remove - remove a hash of the given type
171 //-------------------------------------------------
172
remove(char type)173 bool hash_collection::remove(char type)
174 {
175 bool result = false;
176
177 // handle CRCs
178 if (type == HASH_CRC)
179 {
180 result = m_has_crc32;
181 m_has_crc32 = false;
182 }
183
184 // handle SHA1s
185 else if (type == HASH_SHA1)
186 {
187 result = m_has_sha1;
188 m_has_sha1 = false;
189 }
190 return result;
191 }
192
193
194 //-------------------------------------------------
195 // internal_string - convert set of hashes and
196 // flags to a string in our internal compact
197 // format
198 //-------------------------------------------------
199
internal_string() const200 std::string hash_collection::internal_string() const
201 {
202 std::string buffer;
203
204 // handle CRCs
205 if (m_has_crc32) {
206 buffer.push_back(HASH_CRC);
207 buffer.append(m_crc32.as_string());
208 }
209 // handle SHA1s
210 if (m_has_sha1) {
211 buffer.push_back(HASH_SHA1);
212 buffer.append(m_sha1.as_string());
213 }
214
215 // append flags
216 buffer.append(m_flags);
217 return buffer;
218 }
219
220
221 //-------------------------------------------------
222 // macro_string - convert set of hashes and
223 // flags to a string in the macroized format
224 //-------------------------------------------------
225
macro_string() const226 std::string hash_collection::macro_string() const
227 {
228 std::string buffer;
229
230 // handle CRCs
231 if (m_has_crc32)
232 buffer.append("CRC(").append(m_crc32.as_string()).append(") ");
233
234 // handle SHA1s
235 if (m_has_sha1)
236 buffer.append("SHA1(").append(m_sha1.as_string()).append(") ");
237
238 // append flags
239 if (flag(FLAG_NO_DUMP))
240 buffer.append("NO_DUMP ");
241 if (flag(FLAG_BAD_DUMP))
242 buffer.append("BAD_DUMP ");
243 strtrimspace(buffer);
244 return buffer;
245 }
246
247
248 //-------------------------------------------------
249 // attribute_string - convert set of hashes and
250 // flags to a string in XML attribute format
251 //-------------------------------------------------
252
attribute_string() const253 std::string hash_collection::attribute_string() const
254 {
255 std::string buffer;
256
257 // handle CRCs
258 if (m_has_crc32)
259 buffer.append("crc=\"").append(m_crc32.as_string()).append("\" ");
260
261 // handle SHA1s
262 if (m_has_sha1)
263 buffer.append("sha1=\"").append(m_sha1.as_string()).append("\" ");
264
265 // append flags
266 if (flag(FLAG_NO_DUMP))
267 buffer.append("status=\"nodump\"");
268 if (flag(FLAG_BAD_DUMP))
269 buffer.append("status=\"baddump\"");
270 strtrimspace(buffer);
271 return buffer;
272 }
273
274
275 //-------------------------------------------------
276 // from_internal_string - convert an internal
277 // compact string to set of hashes and flags
278 //-------------------------------------------------
279
from_internal_string(const char * string)280 bool hash_collection::from_internal_string(const char *string)
281 {
282 assert(string != nullptr);
283
284 // start fresh
285 reset();
286
287 // determine the end of the string
288 const char *stringend = string + strlen(string);
289 const char *ptr = string;
290
291 // loop until we hit it
292 bool errors = false;
293 int skip_digits = 0;
294 while (ptr < stringend)
295 {
296 char c = *ptr++;
297 char uc = toupper(c);
298
299 // non-hex alpha values specify a hash type
300 if (uc >= 'G' && uc <= 'Z')
301 {
302 skip_digits = 0;
303 if (uc == HASH_CRC)
304 {
305 m_has_crc32 = true;
306 errors = !m_crc32.from_string(ptr, stringend - ptr);
307 skip_digits = 2 * sizeof(crc32_t);
308 }
309 else if (uc == HASH_SHA1)
310 {
311 m_has_sha1 = true;
312 errors = !m_sha1.from_string(ptr, stringend - ptr);
313 skip_digits = 2 * sizeof(sha1_t);
314 }
315 else
316 errors = true;
317 }
318
319 // hex values are ignored, though unexpected
320 else if ((uc >= '0' && uc <= '9') || (uc >= 'A' && uc <= 'F'))
321 {
322 if (skip_digits != 0)
323 skip_digits--;
324 else
325 errors = true;
326 }
327
328 // anything else is a flag
329 else if (skip_digits != 0)
330 errors = true;
331 else
332 m_flags.push_back(c);
333 }
334 return !errors;
335 }
336
337
338 //-------------------------------------------------
339 // begin - begin hashing
340 //-------------------------------------------------
341
begin(const char * types)342 void hash_collection::begin(const char *types)
343 {
344 // nuke previous creator and make a new one
345 delete m_creator;
346 m_creator = new hash_creator;
347
348 // by default use all types
349 if (types == nullptr)
350 m_creator->m_doing_crc32 = m_creator->m_doing_sha1 = true;
351
352 // otherwise, just allocate the ones that are specified
353 else
354 {
355 m_creator->m_doing_crc32 = (strchr(types, HASH_CRC) != nullptr);
356 m_creator->m_doing_sha1 = (strchr(types, HASH_SHA1) != nullptr);
357 }
358 }
359
360
361 //-------------------------------------------------
362 // buffer - add the given buffer to the hash
363 //-------------------------------------------------
364
buffer(const uint8_t * data,uint32_t length)365 void hash_collection::buffer(const uint8_t *data, uint32_t length)
366 {
367 assert(m_creator != nullptr);
368
369 // append to each active hash
370 if (m_creator->m_doing_crc32)
371 m_creator->m_crc32_creator.append(data, length);
372 if (m_creator->m_doing_sha1)
373 m_creator->m_sha1_creator.append(data, length);
374 }
375
376
377 //-------------------------------------------------
378 // end - stop hashing
379 //-------------------------------------------------
380
end()381 void hash_collection::end()
382 {
383 assert(m_creator != nullptr);
384
385 // finish up the CRC32
386 if (m_creator->m_doing_crc32)
387 {
388 m_has_crc32 = true;
389 m_crc32 = m_creator->m_crc32_creator.finish();
390 }
391
392 // finish up the SHA1
393 if (m_creator->m_doing_sha1)
394 {
395 m_has_sha1 = true;
396 m_sha1 = m_creator->m_sha1_creator.finish();
397 }
398
399 // nuke the creator
400 delete m_creator;
401 m_creator = nullptr;
402 }
403
404
405 //-------------------------------------------------
406 // copyfrom - copy everything from another
407 // collection
408 //-------------------------------------------------
409
copyfrom(const hash_collection & src)410 void hash_collection::copyfrom(const hash_collection &src)
411 {
412 // copy flags directly
413 m_flags = src.m_flags;
414
415 // copy hashes
416 m_has_crc32 = src.m_has_crc32;
417 m_crc32 = src.m_crc32;
418 m_has_sha1 = src.m_has_sha1;
419 m_sha1 = src.m_sha1;
420
421 // don't copy creators
422 m_creator = nullptr;
423 }
424
425 } // namespace util
426