27#include <wx/filename.h>
28#include <wx/translation.h>
39 FILE* fp = wxFopen( aFileName, wxT(
"rb" ) );
43 THROW_IO_ERROR( wxString::Format(
_(
"Cannot open file '%s'." ), aFileName ) );
46 fseek( fp, 0, SEEK_END );
47 long len = ftell( fp );
53 wxString::Format(
_(
"Cannot determine length of file '%s'." ), aFileName ) );
58 static constexpr long MAX_FILE_SIZE = 1L << 30;
60 if( len > MAX_FILE_SIZE )
63 THROW_IO_ERROR( wxString::Format(
_(
"DipTrace file '%s' is too large (%ld bytes)." ),
70 m_data.resize(
static_cast<size_t>( len ) );
78 fseek( fp, 0, SEEK_SET );
80 size_t bytesRead = fread(
m_data.data(), 1,
m_data.size(), fp );
83 if( bytesRead !=
m_data.size() )
85 THROW_IO_ERROR( wxString::Format(
_(
"Error reading file '%s'." ), aFileName ) );
100 if( aOffset >
m_data.size() )
102 THROW_IO_ERROR( wxString::Format(
_(
"Seek past end of file (offset %zu, size %zu)." ),
103 aOffset,
m_data.size() ) );
141 int raw = (
static_cast<int>( p[0] ) << 16 )
142 | (
static_cast<int>( p[1] ) << 8 )
143 | (
static_cast<int>( p[2] ) );
155 unsigned int raw = (
static_cast<unsigned int>( p[0] ) << 24 )
156 | (
static_cast<unsigned int>( p[1] ) << 16 )
157 | (
static_cast<unsigned int>( p[2] ) << 8 )
158 | (
static_cast<unsigned int>( p[3] ) );
163 return static_cast<int>(
static_cast<int64_t
>( raw ) -
INT4_BIAS );
209 _(
"Unexpected end of file at offset 0x%06zX: need 3 bytes for int3, "
210 "have %zu remaining." ),
215 int raw = (
static_cast<int>( p[0] ) << 16 )
216 | (
static_cast<int>( p[1] ) << 8 )
217 | (
static_cast<int>( p[2] ) );
227 _(
"Unexpected end of file at offset 0x%06zX: need 4 bytes for int4, "
228 "have %zu remaining." ),
233 unsigned int raw = (
static_cast<unsigned int>( p[0] ) << 24 )
234 | (
static_cast<unsigned int>( p[1] ) << 16 )
235 | (
static_cast<unsigned int>( p[2] ) << 8 )
236 | (
static_cast<unsigned int>( p[3] ) );
239 return static_cast<int>(
static_cast<int64_t
>( raw ) -
INT4_BIAS );
248 _(
"Unexpected end of file at offset 0x%06zX: need 1 byte." ),
m_offset ) );
260 return static_cast<int>(
static_cast<int64_t
>( aDipTraceCoord ) * 100 / 3 );
274 size_t aStart,
size_t aEnd )
const
276 if( aEnd == 0 || aEnd >
m_data.size() )
279 if( aStart >= aEnd || aPatternLen == 0 || aPatternLen > ( aEnd - aStart ) )
280 return std::string::npos;
282 auto it = std::search(
m_data.begin() + aStart,
285 aPattern + aPatternLen );
287 if( it ==
m_data.begin() + aEnd )
288 return std::string::npos;
290 return static_cast<size_t>( it -
m_data.begin() );
297 return std::string::npos;
301 wxMBConvUTF16BE conv;
305 size_t charCount = aStr.length();
306 std::vector<uint8_t> encoded( charCount * 2 );
308 for(
size_t i = 0; i < charCount; i++ )
311 encoded[i * 2] =
static_cast<uint8_t
>( ( ch >> 8 ) & 0xFF );
312 encoded[i * 2 + 1] =
static_cast<uint8_t
>( ch & 0xFF );
316 size_t matchPos =
FindPattern( encoded.data(), encoded.size(), aStart, aEnd );
318 if( matchPos == std::string::npos )
319 return std::string::npos;
323 return std::string::npos;
368 if( asciiOk && !utf16Ok )
370 else if( utf16Ok && !asciiOk )
384 int charCount = (
static_cast<int>( p[0] ) << 8 ) |
static_cast<int>( p[1] );
393 _(
"Unreasonable string length %d at offset 0x%06zX." ),
397 size_t byteCount =
static_cast<size_t>( charCount ) * 2;
403 wxMBConvUTF16BE conv;
422 _(
"Unreasonable v37 string length %d at offset 0x%06zX." ),
426 size_t count =
static_cast<size_t>( byteCount );
431 wxString
result = wxString::From8BitData(
446 int charCount = (
static_cast<int>( p[0] ) << 8 ) |
static_cast<int>( p[1] );
451 aResult = wxString();
455 if( charCount < 0 || charCount > 500 )
461 size_t byteCount =
static_cast<size_t>( charCount ) * 2;
469 wxMBConvUTF16BE conv;
470 wxString candidate = wxString(
reinterpret_cast<const char*
>( &
m_data[
m_offset] ),
493 int byteCount = (
static_cast<int>( p[0] ) << 16 )
494 | (
static_cast<int>( p[1] ) << 8 )
495 | (
static_cast<int>( p[2] ) );
501 aResult = wxString();
505 if( byteCount < 0 || byteCount > 500 )
511 size_t count =
static_cast<size_t>( byteCount );
519 wxString candidate = wxString::From8BitData(
536 for(
size_t i = 0; i < aStr.length(); i++ )
540 if( ch ==
'\r' || ch ==
'\n' || ch ==
'\t' )
556 _(
"Unexpected end of file at offset 0x%06zX: need %zu bytes, have %zu remaining." ),
557 m_offset, aBytesNeeded, remaining ) );
bool TryReadStringUTF16(wxString &aResult)
Attempt to read a UTF-16-BE string with validation.
size_t FindString(const wxString &aStr, size_t aStart, size_t aEnd) const
Search for a UTF-16-BE encoded string in the file data, including its two-byte length prefix.
int PeekInt3() const
Peek at the next 3-byte biased integer without advancing the position.
wxString ReadStringASCII()
Read a v37 legacy ASCII string: int3(byte_count) + raw ASCII bytes.
bool TryReadStringASCII(wxString &aResult)
Attempt to read a legacy ASCII string with validation.
void ReadBytes(uint8_t *aDst, size_t aCount)
Read a block of raw bytes into the caller's buffer.
int m_version
DipTrace format version.
void Skip(size_t aBytes)
Advance the read position by the given number of bytes.
uint8_t ReadByte()
Read a single unsigned byte and advance the position by 1.
size_t m_offset
Current read position (byte offset).
uint8_t PeekByte() const
Peek at the next byte without advancing the position.
void ThrowEOFError(size_t aBytesNeeded) const
Throw IO_ERROR with a message indicating a read past end of file.
void DetectStringEncoding(size_t aProbeOffset)
Detect the string encoding from the bytes at aProbeOffset, which must sit at the start of a non-empty...
STRING_ENCODING m_stringEncoding
Explicit string encoding override.
static bool IsPrintableString(const wxString &aStr)
Verify that all characters in aStr are printable or common whitespace (space, tab,...
void ReadColor(uint8_t &r, uint8_t &g, uint8_t &b)
Read a 3-byte RGB color value.
int ReadInt4()
Read a 4-byte big-endian biased integer (bias 1,000,000,000) and advance the position by 4.
int ReadInt3()
Read a 3-byte big-endian biased integer (bias 1,000,000) and advance the position by 3.
static int DipTraceToKiCadNm(int aDipTraceCoord)
Convert a DipTrace coordinate value (10 nm units) to KiCad nanometers.
size_t FindPattern(const uint8_t *aPattern, size_t aPatternLen, size_t aStart, size_t aEnd) const
Search for a byte pattern in the file data.
void SetOffset(size_t aOffset)
Set the read position to an absolute byte offset.
bool TryReadString(wxString &aResult)
Attempt to read a string at the current position.
wxString ReadStringUTF16()
Read a v39+ UTF-16-BE string: uint16-BE char count + UTF-16-BE data.
BINARY_READER(const wxString &aFileName)
Construct a reader by loading the given file into memory.
int PeekInt4() const
Peek at the next 4-byte biased integer without advancing the position.
static double DipTraceToMM(int aDipTraceCoord)
Convert a DipTrace coordinate value (10 nm units) to millimeters.
wxString ReadString()
Read a string using the configured encoding.
std::vector< uint8_t > m_data
Entire file contents loaded into memory.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
constexpr double DIPTRACE_COORD_TO_MM
DipTrace uses 762 units per mil (30 000 units per mm).
constexpr int MAX_STRING_CHARS
Maximum sane string length (in characters) accepted by the reader.
constexpr int INT4_BIAS
Bias value added to stored 4-byte unsigned integers.
constexpr int LEGACY_STRING_VERSION
Format version at or below which strings use the legacy ASCII encoding (int3 byte-count + raw ASCII b...
constexpr int INT3_BIAS
Bias value added to stored 3-byte unsigned integers.
wxString result
Test unit parsing edge cases and error handling.