/*****************************************************************************/
/* LibreDWG - free implementation of the DWG file format */
/* */
/* Copyright (C) 2009-2010,2018-2026 Free Software Foundation, Inc. */
/* */
/* This library is free software, licensed under the terms of the GNU */
/* General Public License as published by the Free Software Foundation, */
/* either version 3 of the License, or (at your option) any later version. */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see . */
/*****************************************************************************/
/*
* decode.c: decoding functions
* written by Felipe Castro
* modified by Felipe Corrêa da Silva Sances
* modified by Rodrigo Rodrigues da Silva
* modified by Till Heuschmann
* modified by Reini Urban
* modified by Denis Pruchkovsky
* modified by Michal Josef Špaček
*/
#define _DEFAULT_SOURCE 1 // for endian byteswaps
#define _BSD_SOURCE 1
#define _GNU_SOURCE 1 /* for memmem on linux */
#ifdef __STDC_ALLOC_LIB__
# define __STDC_WANT_LIB_EXT2__ 1 /* for strdup */
#else
# define _USE_BSD 1
#endif
#include "config.h"
#include
#include
#include
#include
#include
#include
#define IS_DECODER
#include "common.h"
#include "bits.h"
#include "dwg.h"
#include "hash.h"
#include "decode.h"
#include "acds.h"
#include "print.h"
#include "free.h"
#include "dynapi.h"
/* the current version per spec block */
static int cur_ver = 0;
static BITCODE_BL rcount1 = 0, rcount2 = 0;
static bool is_teigha = false;
#ifdef DWG_ABORT
static unsigned int errors = 0;
# ifndef DWG_ABORT_LIMIT
# define DWG_ABORT_LIMIT 200
# endif
#endif
#ifdef USE_TRACING
/* This flag means we have checked the environment variable
LIBREDWG_TRACE and set `loglevel' appropriately. */
static bool env_var_checked_p;
#endif /* USE_TRACING */
#define DWG_LOGLEVEL loglevel
#include "logging.h"
#include "dec_macros.h"
// #undef LOG_POS
// #define LOG_POS LOG_INSANE (" @%" PRIuSIZE ".%u\n", dat->byte, dat->bit);
/*------------------------------------------------------------------------------
* Private functions
*/
static int decode_R13_R2000 (Bit_Chain *restrict dat, Dwg_Data *restrict dwg);
static int decode_R2004 (Bit_Chain *restrict dat, Dwg_Data *restrict dwg);
static int decode_R2007 (Bit_Chain *restrict dat, Dwg_Data *restrict dwg);
static int dwg_decode_ole2 (Dwg_Entity_OLE2FRAME *restrict _obj);
static int dwg_decode_common_entity_handle_data (Bit_Chain *dat,
Bit_Chain *hdl_dat,
Dwg_Object *restrict obj);
static int resolve_objectref_vector (Bit_Chain *restrict dat,
Dwg_Data *restrict dwg);
static int dwg_validate_entity_links (Dwg_Data *restrict dwg);
static int secondheader_private (Bit_Chain *restrict dat,
Dwg_Data *restrict dwg);
static int objfreespace_private (Bit_Chain *restrict dat,
Dwg_Data *restrict dwg);
/*----------------------------------------------------------------------------
* Public variables
*/
// long unsigned int ktl_lastaddress;
/*----------------------------------------------------------------------------
* Public function definitions
*/
/** dwg_decode
* returns 0 on success.
*
* everything in dwg is cleared
* and then either read from dat, or set to a default.
*/
EXPORT int
dwg_decode (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
char magic[11];
dwg->num_object_refs = 0;
// dwg->num_layers = 0; // see now dwg->layer_control->num_entries
dwg->num_entities = 0;
dwg->num_objects = 0;
dwg->num_classes = 0;
dwg->thumbnail.size = 0;
dwg->thumbnail.chain = NULL;
dwg->header.num_sections = 0;
dwg->header.section_infohdr.num_desc = 0;
dwg->dwg_class = NULL;
dwg->object_ref = NULL;
dwg->object = NULL;
if (dwg->object_ordered_ref)
{
free (dwg->object_ordered_ref);
dwg->object_ordered_ref = NULL;
}
dwg->num_object_ordered_refs = 0;
dwg->object_map = hash_new (dat->size / 1000);
if (!dwg->object_map)
{
// whatever, we are obviously on a tiny system
dwg->object_map = hash_new (1024);
if (!dwg->object_map)
{
LOG_ERROR ("Out of memory");
return DWG_ERR_OUTOFMEM;
}
}
dwg->dirty_refs = 1;
// memset (&dwg->header, 0, sizeof (dwg->header)); // no, needed for magic
memset (&dwg->header_vars, 0, sizeof (dwg->header_vars));
memset (&dwg->summaryinfo, 0, sizeof (dwg->summaryinfo));
memset (&dwg->fhdr.r2004_header, 0, sizeof (dwg->fhdr.r2004_header));
memset (&dwg->auxheader, 0, sizeof (dwg->auxheader));
memset (&dwg->secondheader, 0, sizeof (dwg->secondheader));
memset (&dwg->objfreespace, 0, sizeof (dwg->objfreespace));
if (dwg->opts)
{
loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
dat->opts = dwg->opts;
}
#ifdef USE_TRACING
/* Before starting, set the logging level, but only do so once. */
if (!env_var_checked_p)
{
char *probe = getenv ("LIBREDWG_TRACE");
if (probe)
loglevel = atoi (probe);
env_var_checked_p = true;
}
#endif /* USE_TRACING */
/* Version */
dat->byte = 0;
dat->bit = 0;
if (!dat->chain
|| dat->size < 58) // saw the smallest r2.10 DWG with 1095 bytes
{
LOG_ERROR ("dwg too small: %" PRIuSIZE " bytes", dat->size);
return DWG_ERR_INVALIDDWG;
}
strncpy (magic, (const char *)dat->chain, 11);
magic[10] = '\0';
dwg->header.from_version = dwg_version_hdr_type (magic);
if (!dwg->header.from_version)
{
if (strncmp (magic, "AC", 2)) // let's ignore MC0.0 for now
{
LOG_ERROR ("Invalid DWG, magic: %s", magic);
}
else
{
LOG_ERROR ("Invalid or unimplemented DWG version code %s", magic);
}
return DWG_ERR_INVALIDDWG;
}
dat->from_version = dwg->header.from_version;
if (!dwg->header.version) // target version not set
{
dat->version = dwg->header.version = dat->from_version;
}
LOG_INFO ("This file's version code is: %s (%s)\n", magic,
dwg_version_type (dat->from_version));
dat->byte = 0xb; // After magic string.
PRE (R_13b1)
{
Dwg_Object *ctrl;
int error = decode_preR13 (dat, dwg);
if (error <= DWG_ERR_CRITICAL)
{
ctrl = &dwg->object[0];
dwg->block_control = *ctrl->tio.object->tio.BLOCK_CONTROL;
}
return error;
}
VERSIONS (R_13b1, R_2000)
{
return decode_R13_R2000 (dat, dwg);
}
VERSIONS (R_2004a, R_2004)
{
return decode_R2004 (dat, dwg);
}
VERSIONS (R_2007a, R_2007)
{
return decode_R2007 (dat, dwg);
}
SINCE (R_2010b)
{
read_r2007_init (dwg); // sets loglevel only for now
return decode_R2004 (dat, dwg);
}
// This line should not be reached
LOG_ERROR ("LibreDWG does not support this DWG version: %s (%s).", magic,
dwg_version_type (dat->from_version));
return DWG_ERR_INVALIDDWG;
}
/* ODA 3.2.6 SECTION-LOCATOR RECORDS: p.21
This is an ODA calculation mistake, it's not needed at all.
static BITCODE_RS
xor_section_CRC (BITCODE_RL num_sections, BITCODE_RS crc)
{
switch (num_sections)
{
case 3:
crc ^= 0xA598;
break;
case 4:
crc ^= 0x8101;
break;
case 5:
crc ^= 0x3CC4;
break;
case 6:
crc ^= 0x8461;
break;
default:
LOG_WARN ("Unknown num_sections " FORMAT_RL ", wrong Section CRC",
num_sections);
}
return crc;
}
*/
// may return OUTOFBOUNDS
static int
template_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_Template *_obj = &dwg->Template;
Dwg_Object *obj = NULL;
int error = 0;
// clang-format off
#include "template.spec"
// clang-format on
dwg->header_vars.MEASUREMENT = _obj->MEASUREMENT;
LOG_TRACE ("-> HEADER.MEASUREMENT: " FORMAT_BS " (0 English/1 Metric)\n",
dwg->header_vars.MEASUREMENT);
return error;
}
static int
decode_R13_R2000 (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Dwg_Object *obj = NULL;
unsigned int section_size = 0;
BITCODE_RS crc, crc2;
size_t size, startpos, endpos, lastmap, pvz = 0;
size_t object_begin, object_end;
BITCODE_BL j;
int error = 0;
int sentinel_size = 16;
const char *section_names[]
= { "AcDb:Header", "AcDb:Classes", "AcDb:Handles",
"AcDb:ObjFreeSpace", "AcDb:Template", "AcDb:AuxHeader" };
{
Dwg_Header *_obj = &dwg->header;
Bit_Chain *hdl_dat = dat;
int i;
BITCODE_BL vcount;
assert (dat->byte == 0xb); // after version magic
// clang-format off
#include "header.spec"
// clang-format on
}
if ((error = dwg_sections_init (dwg)))
return error;
if (dat->byte != 0x19)
{
LOG_ERROR ("Wrong HEADER Section Locator Records at %" PRIuSIZE,
dat->byte);
return DWG_ERR_INVALIDDWG;
}
assert (dat->byte == 0x19);
/* section 0: header vars
* 1: class section
* 2: Handles (object map)
* 3: optional: ObjFreeSpace
* -: 2ndHeader and its sentinels
* 4: optional: Template (MEASUREMENT)
* 5: optional: AuxHeader (no sentinels, since R13c3)
*/
for (j = 0; j < dwg->header.sections; j++)
{
dwg->header.section[j].number = (BITCODE_RLd)bit_read_RC (dat);
dwg->header.section[j].address = (BITCODE_RLL)bit_read_RL (dat);
dwg->header.section[j].size = bit_read_RL (dat);
if (j < 6)
strcpy (dwg->header.section[j].name, section_names[j]);
LOG_TRACE ("section[%u].number: %8d [RC] %s\n", j,
(int)dwg->header.section[j].number,
dwg->header.section[j].name);
LOG_TRACE ("section[%u].address: %8u [RL]\n", j,
(unsigned)dwg->header.section[j].address);
LOG_TRACE ("section[%u].size: %8u [RL]\n", j,
(unsigned)dwg->header.section[j].size);
if (dwg->header.section[j].address + dwg->header.section[j].size
> dat->size)
{
LOG_ERROR ("section[%u] address or size overflow: %" PRIu64
" + " FORMAT_RL " > %" PRIuSIZE,
j, dwg->header.section[j].address,
dwg->header.section[j].size, dat->size);
return DWG_ERR_INVALIDDWG;
}
}
// Check CRC up to now (note: ODA has a bug here)
crc2 = bit_calc_CRC (0xC0C1, &dat->chain[0], dat->byte); // from 0 to now
crc = bit_read_RS (dat);
LOG_TRACE ("crc: %04X [RSx] from 0-%" PRIuSIZE "\n", (unsigned)crc,
dat->byte - 2);
if (crc != crc2)
{
LOG_ERROR ("Header CRC mismatch %04X <=> %04X", (unsigned)crc,
(unsigned)crc2);
error |= DWG_ERR_WRONGCRC;
}
if (bit_search_sentinel (dat, dwg_sentinel (DWG_SENTINEL_HEADER_END)))
LOG_TRACE (" HEADER (end): %4u\n", (unsigned)dat->byte);
/*-------------------------------------------------------------------------
* Section 5 AuxHeader
* R13c3+, mostly redundant file header information. no sentinels
*/
if (dwg->header.sections == 6 && dwg->header.version >= R_13c3)
{
Dwg_AuxHeader *_obj = &dwg->auxheader;
Bit_Chain *hdl_dat = dat;
size_t end_address = dwg->header.section[SECTION_AUXHEADER_R2000].address
+ dwg->header.section[SECTION_AUXHEADER_R2000].size;
obj = NULL;
dat->byte = dwg->header.section[SECTION_AUXHEADER_R2000].address;
LOG_TRACE ("\n"
"=======> AuxHeader: %4zu\n",
dat->byte);
LOG_TRACE (" AuxHeader (end): %4zu\n", end_address);
if (dat->size < end_address)
{
LOG_ERROR ("Invalid AuxHeader size: buffer overflow");
error |= DWG_ERR_SECTIONNOTFOUND;
}
else
{
size_t old_size = dat->size;
BITCODE_BL vcount;
dat->size = end_address;
// clang-format off
#include "auxheader.spec"
// clang-format on
dat->size = old_size;
}
}
/*-------------------------------------------------------------------------
* Thumbnail Image (pre-r13c3 before, since r13c3 at the end)
*/
if (bit_search_sentinel (dat, dwg_sentinel (DWG_SENTINEL_THUMBNAIL_BEGIN)))
{
size_t start_address;
dat->bit = 0;
start_address = dat->byte;
LOG_TRACE ("\n=======> Thumbnail: %4zu\n", start_address - 16);
if (dwg->header.thumbnail_address
&& dwg->header.thumbnail_address != (BITCODE_RL)(dat->byte - 16))
LOG_WARN ("Illegal header.thumbnail_address: %i != %" PRIuSIZE,
dwg->header.thumbnail_address, dat->byte - 16);
dwg->header.thumbnail_address = (dat->byte - 16) & 0xFFFFFFFF;
if (bit_search_sentinel (dat, dwg_sentinel (DWG_SENTINEL_THUMBNAIL_END)))
{
BITCODE_RL bmpsize;
LOG_TRACE (" Thumbnail (end): %4zu\n", dat->byte);
if ((dat->byte - 16) < start_address)
{
LOG_ERROR ("Illegal header.thumbnail_size: %" PRIuSIZE
" < %" PRIuSIZE,
dat->byte - 16, start_address);
}
else if ((dat->byte - 16) - start_address < 10)
{
LOG_TRACE ("No header.thumbnail: %" PRIuSIZE " < 10",
dat->byte - 16 - start_address);
}
else
{
BITCODE_RC type;
assert ((dat->byte - 16) >= start_address);
dwg->thumbnail.size = (dat->byte - 16) - start_address;
dwg->thumbnail.chain
= (unsigned char *)calloc (dwg->thumbnail.size, 1);
dwg->thumbnail.byte = 0;
if (!dwg->thumbnail.chain)
{
LOG_ERROR ("Out of memory");
return DWG_ERR_OUTOFMEM;
}
memcpy (dwg->thumbnail.chain, &dat->chain[start_address],
dwg->thumbnail.size);
dat->byte += dwg->thumbnail.size;
dwg_bmp (dwg, &bmpsize, &type);
if (bmpsize > dwg->thumbnail.size)
LOG_ERROR ("thumbnail size overflow: %i > %" PRIuSIZE "\n",
bmpsize, dwg->thumbnail.size);
}
}
}
/*-------------------------------------------------------------------------
* Header Variables, section 0
*/
LOG_INFO ("\n"
"=======> Header Variables: %4u\n",
(unsigned int)dwg->header.section[SECTION_HEADER_R13].address);
LOG_INFO (" Header Variables (end): %4u\n",
(unsigned int)(dwg->header.section[SECTION_HEADER_R13].address
+ dwg->header.section[SECTION_HEADER_R13].size));
if (dwg->header.section[SECTION_HEADER_R13].address < 58
|| dwg->header.section[SECTION_HEADER_R13].address
+ dwg->header.section[SECTION_HEADER_R13].size
> dat->size)
{
LOG_ERROR ("Invalid Header section, skipped");
error |= DWG_ERR_SECTIONNOTFOUND;
goto classes_section;
}
// after sentinel
dat->byte = pvz = dwg->header.section[SECTION_HEADER_R13].address + 16;
// LOG_HANDLE ("@ 0x" FORMAT_HV ".%" PRIuSIZE "\n", bit_position (dat)/8,
// bit_position (dat)%8);
#define MAX_HEADER_SIZE 2048
dwg->header_vars.size = bit_read_RL (dat);
LOG_TRACE (" Length: " FORMAT_RL " [RL]\n", dwg->header_vars.size);
if (dwg->header_vars.size > MAX_HEADER_SIZE)
{
LOG_WARN ("Fixup illegal Header Length");
dwg->header_vars.size = dwg->header.section[SECTION_HEADER_R13].size;
if (dwg->header_vars.size > 20)
dwg->header_vars.size -= (16 + 4);
}
dat->bit = 0;
error |= dwg_decode_header_variables (dat, dat, dat, dwg);
// LOG_HANDLE ("@ 0x" FORMAT_HV ".%" PRIuSIZE "\n", bit_position (dat)/8,
// bit_position (dat)%8); check slack Check CRC, hardcoded to 2 before end
// sentinel
if (dwg->header_vars.size < MAX_HEADER_SIZE)
{
size_t crcpos = pvz + dwg->header_vars.size + 4;
if (dat->bit || dat->byte != crcpos)
{
unsigned char r = 8 - dat->bit;
LOG_HANDLE (" padding: %zd byte, %d bits\n", crcpos - dat->byte, r);
}
LOG_HANDLE (" crc pos: %" PRIuSIZE "\n", crcpos);
bit_set_position (dat, crcpos * 8);
crc = bit_read_RS (dat);
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_HANDLE)
LOG_HANDLE ("crc: %04X [RSx] from %" PRIuSIZE "-%" PRIuSIZE "=%zd\n",
crc, pvz, dat->byte - 2, dat->byte - 2 - pvz);
else
LOG_TRACE ("crc: %04X [RSx] %zd\n", crc, dat->byte - 2 - pvz);
}
else
{
LOG_WARN ("Skip crc with illegal Header Length");
error |= DWG_ERR_SECTIONNOTFOUND;
goto classes_section;
}
crc2 = 0;
// LOG_HANDLE ("@ 0x" FORMAT_HV "\n", bit_position (dat)/8);
// LOG_HANDLE ("HEADER_R13.address of size 0x" FORMAT_HV "\n", pvz);
// LOG_HANDLE ("HEADER_R13.size %d\n",
// dwg->header.section[SECTION_HEADER_R13].size);
// typical sizes: 400-599
if (dwg->header.section[SECTION_HEADER_R13].size > 34
&& dwg->header.section[SECTION_HEADER_R13].size < 0xfff
&& pvz < dat->byte
&& pvz + dwg->header.section[SECTION_HEADER_R13].size < dat->size)
{
// not dwg->header_vars.size, rather -4 (minus the section_size).
// section_size + data, i.e. minus the 2x sentinel (32) + crc itself (2)
// if we would include the crc we would always get 0000
BITCODE_RL crc_size = dwg->header.section[SECTION_HEADER_R13].size - 34;
LOG_HANDLE (" calc header crc size: " FORMAT_RL "\n", crc_size);
crc2 = bit_calc_CRC (0xC0C1, &dat->chain[pvz], crc_size);
}
if (crc != crc2)
{
LOG_WARN ("Header Section[%d] CRC mismatch %04X <=> %04X",
(int)dwg->header.section[SECTION_HEADER_R13].number, crc,
crc2);
error |= DWG_ERR_WRONGCRC;
}
/*-------------------------------------------------------------------------
* Classes, section 1
*/
classes_section:
LOG_INFO ("\n"
"=======> Classes (start): %4lu\n",
(long)dwg->header.section[SECTION_CLASSES_R13].address);
LOG_INFO (" Classes (end) : %4lu\n",
(long)(dwg->header.section[SECTION_CLASSES_R13].address
+ dwg->header.section[SECTION_CLASSES_R13].size));
dat->byte = dwg->header.section[SECTION_CLASSES_R13].address;
if (dwg->header.section[SECTION_CLASSES_R13].size < 30)
{
LOG_ERROR ("Classes section too small, skipped");
error |= DWG_ERR_SECTIONNOTFOUND;
goto handles_section;
}
// check sentinel
if (memcmp (dwg_sentinel (DWG_SENTINEL_CLASS_BEGIN), &dat->chain[dat->byte],
16)
== 0)
{
dat->byte += 16;
}
else
{
sentinel_size = 0;
LOG_TRACE ("no class sentinel\n");
}
dat->bit = 0;
size = bit_read_RL (dat);
LOG_TRACE (" Size : %" PRIuSIZE " [RL]\n", size);
if (size
!= dwg->header.section[SECTION_CLASSES_R13].size
- ((sentinel_size * 2) + 6))
{
endpos = dwg->header.section[SECTION_CLASSES_R13].address
+ dwg->header.section[SECTION_CLASSES_R13].size - sentinel_size;
LOG_ERROR ("Invalid size %" PRIuSIZE ", should be: " FORMAT_RL
", endpos: %" PRIuSIZE "\n",
size,
dwg->header.section[SECTION_CLASSES_R13].size
- ((sentinel_size * 2) + 6),
endpos);
error |= DWG_ERR_SECTIONNOTFOUND;
goto handles_section;
}
else
endpos = dat->byte + size;
LOG_INSANE ("endpos: %" PRIuSIZE, endpos);
LOG_POS_ (INSANE);
/* Read the classes
*/
dwg->layout_type = 0;
dwg->num_classes = 0;
#if 0
SINCE (R_2004a) // dead code. see read_2004_section_classes() instead
{
BITCODE_B btrue;
BITCODE_BS max_num;
BITCODE_RS rs_zero;
max_num = bit_read_BS (dat);
LOG_TRACE ("2004 max_num: " FORMAT_BS " [BS]\n", max_num);
rs_zero = bit_read_RS (dat);
LOG_TRACE ("2004 rs_zero: " FORMAT_RS " [RS]\n", rs_zero);
btrue = bit_read_B (dat); // always 1
LOG_TRACE ("2004 btrue: " FORMAT_B " [B]\n", btrue);
}
#endif
while (dat->byte < endpos - 1)
{
BITCODE_BS i;
Dwg_Class *klass;
i = dwg->num_classes;
/* CWE-400: bound max classes to prevent DoS via crafted input.
Each class entry is at minimum ~10 bytes on disk;
sizeof(Dwg_Class) is a very generous upper bound. */
if ((size_t)i >= 100 + (size / sizeof (Dwg_Class)) || i >= 65535)
{
LOG_ERROR ("Too many classes: %u, max %" PRIuSIZE, (unsigned)i,
100 + (size / sizeof (Dwg_Class)));
error |= DWG_ERR_VALUEOUTOFBOUNDS;
goto handles_section;
}
if (i == 0)
dwg->dwg_class = (Dwg_Class *)malloc (sizeof (Dwg_Class));
else
dwg->dwg_class = (Dwg_Class *)realloc (dwg->dwg_class,
(i + 1) * sizeof (Dwg_Class));
if (!dwg->dwg_class)
{
LOG_ERROR ("Out of memory");
return DWG_ERR_OUTOFMEM;
}
klass = &dwg->dwg_class[i];
memset (klass, 0, sizeof (Dwg_Class));
klass->number = bit_read_BS (dat);
LOG_HANDLE ("-------------------\n");
LOG_HANDLE ("Number: " FORMAT_BS " [BS]", klass->number);
LOG_POS_ (HANDLE);
klass->proxyflag = bit_read_BS (dat);
LOG_HANDLE ("Proxyflag: " FORMAT_BS " [BS]", klass->proxyflag);
LOG_POS_ (HANDLE);
dwg_log_proxyflag (DWG_LOGLEVEL, DWG_LOGLEVEL_HANDLE, klass->proxyflag);
if (dat->byte >= endpos)
break;
klass->appname = bit_read_TV (dat);
LOG_HANDLE ("Application name: \"%s\" [TV]", klass->appname);
LOG_POS_ (HANDLE);
if (dat->byte >= endpos)
{
free (klass->appname);
break;
}
klass->cppname = bit_read_TV (dat);
LOG_HANDLE ("C++ class name: %s [TV] ", klass->cppname);
LOG_POS_ (HANDLE);
klass->dxfname = bit_read_TV (dat);
LOG_HANDLE ("DXF record name: %s [TV] ", klass->dxfname);
LOG_POS_ (HANDLE);
klass->is_zombie = bit_read_B (dat); // was_a_proxy
LOG_HANDLE ("is_zombie: " FORMAT_B " [B] ", klass->is_zombie);
LOG_POS_ (HANDLE);
// 1f2 for entities, 1f3 for objects
klass->item_class_id = bit_read_BS (dat);
LOG_HANDLE ("item_class_id: " FORMAT_BS " [BS]",
klass->item_class_id);
LOG_POS_ (HANDLE);
if (DWG_LOGLEVEL == DWG_LOGLEVEL_TRACE)
{
LOG (TRACE,
"Class %d 0x%x %s\n"
" %s \"%s\" %d 0x%x\n",
klass->number, klass->proxyflag, klass->dxfname, klass->cppname,
klass->appname, klass->is_zombie, klass->item_class_id);
}
#if 0
SINCE (R_2007a) //? dead code it seems. see read_2004_section_classes()
{
klass->num_instances = bit_read_BL (dat);
LOG_HANDLE ("num_instances: " FORMAT_BL " [BL]", klass->num_instances); LOG_POS_ (HANDLE);
klass->dwg_version = bit_read_BL (dat); // nope: class_version
klass->maint_version = bit_read_BL (dat);
klass->unknown_1 = bit_read_BL (dat);
klass->unknown_2 = bit_read_BL (dat);
LOG_TRACE (
" num_instances: %d, dwg/maint version: %d/%d, unk: %d/%d\n",
klass->num_instances, klass->dwg_version, klass->maint_version,
klass->unknown_1, klass->unknown_2);
}
#endif
if (klass->dxfname && strEQc ((const char *)klass->dxfname, "LAYOUT"))
dwg->layout_type = klass->number;
dwg->num_classes++;
}
LOG_HANDLE ("-------------------\n");
// Check Section CRC
dat->byte = dwg->header.section[SECTION_CLASSES_R13].address
+ dwg->header.section[SECTION_CLASSES_R13].size - 18;
dat->bit = 0;
pvz = dwg->header.section[SECTION_CLASSES_R13].address + 16;
if (!bit_check_CRC (dat, pvz, 0xC0C1))
error |= DWG_ERR_WRONGCRC;
dat->byte += 16; // sentinel
pvz = bit_read_RL (dat); // Unknown bitlong inter class and object
LOG_TRACE ("unknown: 0x%04zx [RL] @%" PRIuSIZE "\n", pvz, dat->byte - 4);
LOG_INFO ("Number of classes read: %u\n", dwg->num_classes);
/*-------------------------------------------------------------------------
* Object-map, section 2
*/
handles_section:
dat->byte = dwg->header.section[SECTION_HANDLES_R13].address;
dat->bit = 0;
lastmap = dat->byte + dwg->header.section[SECTION_HANDLES_R13].size; // 4
dwg->num_objects = 0;
object_begin = dat->size;
object_end = 0;
LOG_INFO ("\n"
"=======> Handles (start) : %8u\n",
(unsigned int)dwg->header.section[SECTION_HANDLES_R13].address);
LOG_INFO (" Handles (end) : %8u\n",
(unsigned int)(dwg->header.section[SECTION_HANDLES_R13].address
+ dwg->header.section[SECTION_HANDLES_R13].size));
LOG_INFO (" Length: %u\n",
(unsigned int)dwg->header.section[SECTION_HANDLES_R13].size);
do
{
BITCODE_RLL last_handle = 0;
size_t last_offset = 0;
size_t oldpos = 0;
BITCODE_RLL maxh
= (BITCODE_RLL)dwg->header.section[SECTION_HANDLES_R13].size << 1;
BITCODE_RLL max_handles
= maxh < INT32_MAX ? maxh
: dwg->header.section[SECTION_HANDLES_R13].size;
int added;
pvz = dat->byte;
startpos = dat->byte;
section_size = bit_read_RS_BE (dat);
LOG_TRACE ("Handles page size: %u [RS_BE]", section_size);
LOG_HANDLE (" @%" PRIuSIZE, startpos);
LOG_TRACE ("\n");
if (section_size > 2040)
{
LOG_ERROR ("Object-map section size greater than 2040!");
return DWG_ERR_VALUEOUTOFBOUNDS;
}
while (dat->byte - startpos < section_size)
{
BITCODE_MC prevsize;
BITCODE_UMC handleoff;
BITCODE_MC offset;
// BITCODE_RLL last_handle = dwg->num_objects
// ? dwg->object[dwg->num_objects - 1].handle.value : 0;
oldpos = dat->byte;
// The offset from the previous handle. default: 1, unsigned.
// Basically how many objects have been deleted here.
handleoff = bit_read_UMC (dat);
// The offset from the previous address. default: obj->size, signed.
offset = bit_read_MC (dat);
prevsize = dwg->num_objects
? dwg->object[dwg->num_objects - 1].size + 4
: 0L;
if ((handleoff == 0) || (handleoff > (max_handles - last_handle))
|| (offset > -4 && offset < prevsize - 8))
{
if (offset == prevsize)
LOG_WARN ("handleoff " FORMAT_UMC
" looks wrong, max_handles %x - "
"last_handle " FORMAT_HV " = " FORMAT_RLLx
" (@%" PRIuSIZE ")",
handleoff, (unsigned)max_handles, last_handle,
max_handles - last_handle, oldpos);
if (offset == 1
|| (offset > 0 && offset < prevsize && prevsize > 0)
|| (offset < 0 && labs ((long)offset) < prevsize
&& prevsize > 0))
{
if (offset != prevsize)
LOG_WARN ("offset " FORMAT_MC
" looks wrong, should be prevsize " FORMAT_MC
" + 4",
offset, prevsize - 4);
// handleoff = 1;
// offset = prevsize;
// LOG_WARN ("Recover invalid handleoff to %" PRIuSIZE " and
// offset to %ld",
// handleoff, offset);
}
}
last_offset += offset;
LOG_TRACE ("\nNext object: %lu ", (unsigned long)dwg->num_objects);
LOG_TRACE ("Handleoff: " FORMAT_UMC " [UMC]", handleoff);
LOG_HANDLE (" Offset: " FORMAT_MC " [MC] @%" PRIuSIZE, offset,
last_offset);
LOG_TRACE ("\n");
if (dat->byte == oldpos)
break;
if (object_end < last_offset)
object_end = last_offset;
if (object_begin > last_offset)
object_begin = last_offset;
added = dwg_decode_add_object (dwg, dat, dat, last_offset);
if (added > 0)
error |= added; // else not added (skipped) or -1 for re-allocated
if (dwg->num_objects)
last_handle = dwg->object[dwg->num_objects - 1].handle.value;
// LOG_HANDLE ("dat: @%lu.%u\n", dat->byte, dat->bit);
}
if (dat->byte == oldpos)
break;
// CRC on
if (dat->bit > 0)
{
dat->byte += 1;
dat->bit = 0;
}
if (dat->byte >= dat->size)
{
LOG_ERROR ("Handles overflow @%" PRIuSIZE, dat->byte);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
crc = bit_read_RS_BE (dat);
pvz = dat->byte;
LOG_TRACE ("\nHandles page crc: %04X [RSx_BE] (%" PRIuSIZE "-%" PRIuSIZE
" = %u)\n",
crc, startpos, startpos + section_size, section_size);
crc2 = bit_calc_CRC (0xC0C1, dat->chain + startpos, section_size);
if (crc != crc2)
{
LOG_ERROR ("Handles Section[%d] page CRC mismatch %04X <=> %04X",
(int)dwg->header.section[SECTION_HANDLES_R13].number, crc,
crc2);
// fails with r14
// if (dwg->header.version == R_2000)
// return DWG_ERR_WRONGCRC;
if (dat->from_version != R_14)
error |= DWG_ERR_WRONGCRC;
}
if (dat->byte >= lastmap)
break;
}
while (section_size > 2);
LOG_INFO ("Num objects: %lu\n", (unsigned long)dwg->num_objects);
LOG_INFO ("\n"
"=======> Last Object : %8lu\n",
(unsigned long)object_begin);
if (object_end <= dat->size)
dat->byte = object_end;
object_begin = bit_read_MS (dat);
LOG_TRACE ("last object size: %" PRIuSIZE " [MS]", object_begin);
LOG_HANDLE (" (@%" PRIuSIZE ")", object_end);
LOG_TRACE ("\n");
LOG_INFO (" Last Object (end): %8zu\n",
(object_end + object_begin + 2));
/*-------------------------------------------------------------------------
* Section 2: ObjFreeSpace, r13c3-r2000
*/
if (dwg->header.sections > 3
&& (dwg->header.section[SECTION_OBJFREESPACE_R13].address == pvz))
{
dat->byte = dwg->header.section[SECTION_OBJFREESPACE_R13].address;
dat->bit = 0;
LOG_INFO ("\n"
"=======> ObjFreeSpace 3 (start): %4zu\n",
dat->byte);
LOG_INFO (" ObjFreeSpace 3 (end) : %4zu\n",
dat->byte
+ dwg->header.section[SECTION_OBJFREESPACE_R13].size);
error |= objfreespace_private (dat, dwg);
}
/*-------------------------------------------------------------------------
* Second header, r13-r2000 only. With sentinels.
*/
if (bit_search_sentinel (dat, dwg_sentinel (DWG_SENTINEL_2NDHEADER_BEGIN)))
{
struct _dwg_secondheader *_obj = &dwg->secondheader;
const char *const names[] = {
"HANDSEED",
"BLOCK_CONTROL_OBJECT",
"LAYER_CONTROL_OBJECT",
"STYLE_CONTROL_OBJECT",
"LTYPE_CONTROL_OBJECT",
"VIEW_CONTROL_OBJECT",
"UCS_CONTROL_OBJECT",
"VPORT_CONTROL_OBJECT",
"APPID_CONTROL_OBJECT",
"DIMSTYLE_CONTROL_OBJECT",
"VX_CONTROL_OBJECT",
"DICTIONARY_NAMED_OBJECT",
"DICTIONARY_ACAD_MLINESTYLE",
"DICTIONARY_ACAD_GROUP",
};
for (int i = 0; i < ARRAY_SIZE (names); i++)
_obj->handles[i].name = names[i];
LOG_INFO ("\n=======> Second Header (start): %4zu\n", dat->byte);
error |= secondheader_private (dat, dwg);
if (bit_search_sentinel (dat, dwg_sentinel (DWG_SENTINEL_2NDHEADER_END)))
LOG_INFO (" Second Header (end) : %4zu\n", dat->byte);
}
/*-------------------------------------------------------------------------
* Section 4: Template (with MEASUREMENT)
* (Called PADDING section in the ODA)
*/
if (dwg->header.sections > 4)
{
LOG_INFO ("\n"
"=======> Template 4 (start) : %8u\n",
(unsigned int)dwg->header.section[4].address);
LOG_INFO (" Template 4 (end) : %8u\n",
(unsigned int)(dwg->header.section[4].address
+ dwg->header.section[4].size));
dat->byte = dwg->header.section[4].address;
dat->bit = 0;
error |= template_private (dat, dwg);
}
// step II of handles parsing: resolve pointers from handle value
// XXX: move this somewhere else
LOG_INFO ("\nnum_objects: %lu\n", (unsigned long)dwg->num_objects);
LOG_TRACE ("num_object_refs: %lu\n", (unsigned long)dwg->num_object_refs);
LOG_TRACE ("Resolving pointers from ObjectRef vector:\n");
error |= resolve_objectref_vector (dat, dwg);
dwg_validate_entity_links (dwg);
return error;
}
static int
resolve_objectref_vector (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
BITCODE_BL i;
Dwg_Object *obj;
LOG_INSANE ("==========\n");
for (i = 0; i < dwg->num_object_refs; i++)
{
Dwg_Object_Ref *ref = dwg->object_ref[i];
LOG_HANDLE ("-objref[%3ld]: HANDLE" FORMAT_REF "\n", (long)i,
ARGS_REF (ref));
assert (ref->handleref.is_global == 1);
// skip code 0 refs (e.g. HANDSEED), they are handle values, not obj refs
if (ref->handleref.code == 0)
continue;
// search the handle in all objects
obj = dwg_resolve_handle (dwg, ref->absolute_ref);
if (obj)
{
LOG_HANDLE ("-found: HANDLE(" FORMAT_H ") => [%u]\n",
ARGS_H (obj->handle), obj->index);
}
// assign found pointer to objectref vector
ref->obj = obj;
#if 0
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
if (obj)
dwg_print_object (dat, obj);
else
LOG_HANDLE ("Null object pointer: object_ref[%ld]\n", (long)i);
}
#endif
}
dwg->dirty_refs = 0;
return dwg->num_object_refs ? 0 : DWG_ERR_VALUEOUTOFBOUNDS;
}
/* Find the BITCODE_H for an object */
Dwg_Object_Ref *
dwg_find_objectref (const Dwg_Data *restrict dwg,
const Dwg_Object *restrict obj)
{
for (BITCODE_BL i = 0; i < dwg->num_object_refs; i++)
{
Dwg_Object_Ref *ref = dwg->object_ref[i];
Dwg_Object *found = dwg_resolve_handle_silent (dwg, ref->absolute_ref);
if (found == obj)
return ref;
}
return NULL;
}
void
dwg_resolve_objectrefs_silent (Dwg_Data *restrict dwg)
{
const int oldloglevel = loglevel;
loglevel = 0;
// Dwg_Object_Ref->obj are stored all over. dirty it to update dynamically.
// TODO: this is now forever. find a way to resolve all objs also.
dwg->dirty_refs = 1;
for (BITCODE_BL i = 0; i < dwg->num_object_refs; i++)
{
// scan num_objects for the id (absolute_ref)
Dwg_Object *restrict obj
= dwg_resolve_handle_silent (dwg, dwg->object_ref[i]->absolute_ref);
dwg->object_ref[i]->obj = obj;
}
dwg->dirty_refs = 0;
// TODO: scan dwg->num_objects also to update it's handlerefs
loglevel = oldloglevel;
}
/* endian specific */
void
bfr_read_32 (void *restrict dst, BITCODE_RC *restrict *restrict src,
size_t size)
{
size_t n;
uint32_t *dp, *sp, *dp0 = NULL, *sp0 = NULL;
bool dst_unaligned = false;
bool src_unaligned = false;
assert (!(size % 4));
// dst may be misaligned
if ((intptr_t)dst % 4)
{
dst_unaligned = true;
dp0 = dp = (uint32_t *)malloc (size);
}
else
dp = (uint32_t *)dst;
// likewise *src may be misaligned
if ((intptr_t)*src % 4)
{
src_unaligned = true;
sp0 = sp = (uint32_t *)malloc (size);
memcpy ((void *)sp, (const void *)*src, size);
}
else
sp = (uint32_t *)*src;
for (n = 0; n < size / sizeof (uint32_t); n++)
{
*dp++ = le32toh (*sp);
sp++;
}
if (src_unaligned)
free ((void *)sp0);
if (dst_unaligned)
{
memcpy ((void *)dst, (const void *)dp0, size);
free ((void *)dp0);
}
*src += size;
size -= n * sizeof (uint32_t);
assert (size == 0);
}
#if 0
void
bfr_read (void *restrict dst, BITCODE_RC *restrict *restrict src, size_t size)
{
memcpy (dst, *src, size);
*src += size;
}
/* endian specific */
void
bfr_read_64 (void *restrict dst, BITCODE_RC *restrict *restrict src,
size_t size)
{
size_t n;
uint64_t *dp, *sp, *dp0 = NULL, *sp0 = NULL;
bool dst_unaligned = false;
bool src_unaligned = false;
assert (!(size % 8));
// dst may be misaligned
if ((intptr_t)dst % 8)
{
dst_unaligned = true;
dp0 = dp = (uint64_t *)malloc (size);
}
else
dp = (uint64_t *)dst;
// likewise *src may be misaligned
if ((intptr_t)*src % 8)
{
src_unaligned = true;
sp0 = sp = (uint64_t *)malloc (size);
memcpy ((void *)sp, (const void *)*src, size);
}
else
sp = (uint64_t *)src;
for (n = 0; n < size / sizeof (uint64_t); n++)
{
*dp++ = le64toh (*sp);
sp++;
}
if (src_unaligned)
free ((void *)sp0);
if (dst_unaligned)
{
memcpy ((void *)dst, (const void *)dp0, size);
free ((void *)dp0);
}
*src += size;
size -= n * sizeof (uint64_t);
assert (size == 0);
}
#endif
// always byte-aligned
static unsigned char
copy_bytes (unsigned int lit_length, Bit_Chain *restrict src,
Bit_Chain *restrict dst)
{
LOG_INSANE (">c %u %" PRIuSIZE "->%" PRIuSIZE "\n", lit_length, src->byte,
dst->byte);
for (unsigned int i = 0; i < lit_length; ++i)
{
unsigned char b = bit_read_RC (src);
bit_write_RC (dst, b);
}
return bit_read_RC (src);
}
/* R2004 encoded literal length
*/
static unsigned int
read_literal_length (Bit_Chain *restrict dat, unsigned char opcode)
{
unsigned int lowbits = opcode & 0xf;
if (lowbits == 0)
{ // if low bits are 0
BITCODE_RC lastbyte = 0;
while (((lastbyte = bit_read_RC (dat)) == 0) && (dat->byte < dat->size))
{
LOG_INSANE ("L %u\n", lowbits + 3);
return lowbits + 3;
}
/* R2004 Read encoded number of compressed bytes
*/
static int
read_compressed_bytes (Bit_Chain *restrict dat, const unsigned char opcode,
const unsigned bits)
{
unsigned int compressed_bytes = opcode & bits;
if (compressed_bytes == 0)
{
BITCODE_RC lastbyte = 0;
while (((lastbyte = bit_read_RC (dat)) == 0) && (dat->byte < dat->size))
{
LOG_INSANE ("C %u\n", compressed_bytes + 2);
return (int)compressed_bytes + 2;
}
/* R2004 Two Byte Offset
*/
static BITCODE_RC
two_byte_offset (Bit_Chain *restrict dat, int plus, int *restrict offset)
{
BITCODE_RC firstByte = bit_read_RC (dat);
BITCODE_RC secondByte = bit_read_RC (dat);
*offset |= (firstByte >> 2);
*offset |= secondByte << 6;
*offset += plus;
return firstByte;
}
/* Decompresses a system section of a 2004+ DWG file.
* With a LZ77 variant.
*/
int
decompress_R2004_section (Bit_Chain *restrict src, Bit_Chain *restrict dec)
{
unsigned int lit_length;
int comp_offset, comp_bytes;
size_t pos, end;
unsigned char opcode1 = 0, opcode2;
if (src->byte > src->size) // bytes left to read from
{
LOG_WARN ("Invalid comp_data_size %" PRIuSIZE " @%" PRIuSIZE, src->size,
src->byte);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
// length of the first sequence of uncompressed or literal data.
// lit_length = read_literal_length (src, opcode1);
// LOG_INSANE ("L: %u\n", lit_length);
// if ((unsigned long)lit_length > dec->size)
// {
// LOG_ERROR ("Invalid literal_length %u > %lu dec.size", lit_length,
// dec->size)
// return DWG_ERR_VALUEOUTOFBOUNDS;
// }
// bit_read_fixed (src, dec->chain, lit_length);
// dec->byte += lit_length;
// bytes_left -= lit_length;
LOG_INSANE ("(%" PRIuSIZE ")\n", dec->byte);
opcode1 = bit_read_RC (src);
if ((opcode1 & 0xF0) == 0)
{
lit_length = read_literal_length (src, opcode1);
// the literal must fit into the output buffer
if ((size_t)lit_length > dec->size - dec->byte)
{
LOG_ERROR ("Invalid literal_length %u > %" PRIuSIZE " dec.size",
lit_length, dec->size);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
opcode1 = copy_bytes (lit_length, src, dec);
}
while (src->byte < src->size && dec->byte < dec->size && opcode1 != 0x11)
{
LOG_INSANE ("\n(%" PRIuSIZE ") -O %x\n", dec->byte, opcode1);
comp_bytes = 0;
comp_offset = 0;
if (opcode1 < 0x10 || opcode1 >= 0x40) // oda has <0x10 as unused
{
comp_bytes = (opcode1 >> 4) - 1;
opcode2 = bit_read_RC (src);
LOG_INSANE ("> 2) & 3) | (opcode2 << 2)) + 1;
LOG_INSANE ("o: %d %d\n", comp_bytes, comp_offset);
}
else if (opcode1 < 0x20) // 0x12-0x1f
{
comp_bytes = read_compressed_bytes (src, opcode1, 7);
comp_offset = (opcode1 & 8) << 11;
opcode1 = two_byte_offset (src, 0x4000, &comp_offset);
LOG_INSANE ("= 0x20)
{
comp_bytes = read_compressed_bytes (src, opcode1, 0x1f);
opcode1 = two_byte_offset (src, 1, &comp_offset);
LOG_INSANE ("O %x!\n", opcode1);
break; // Terminates the input stream, everything is ok
}
else
{
LOG_ERROR ("Invalid opcode 0x%x in input stream at pos %" PRIuSIZE,
opcode1, src->byte);
return DWG_ERR_INTERNALERROR; // error in input stream
}
// copy previous offset'ed bytes.
pos = dec->byte;
LOG_INSANE ("co: %d %ld->%" PRIuSIZE "\n", comp_bytes,
(long)pos - comp_offset, pos);
// A final back-reference may legitimately fill the buffer exactly to
// dec->size (the trailing copy lands in the unused tail past the real
// page data and is never read). 0.13.3 allowed this (dst + comp_bytes >
// dst_end); the refactor's `>=` rejected the exact-fill case and aborted
// the whole section mid-stream, dropping/garbling thousands of objects
// on real R2018 files. Only error when the copy would overrun the
// buffer.
end = pos + comp_bytes;
if (end > dec->size || (long)pos < comp_offset
|| (size_t)(pos - comp_offset) >= dec->size
|| (size_t)comp_offset > dec->size)
{
LOG_ERROR ("Invalid decompression bytes %d, offset %d", comp_bytes,
comp_offset);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
// GH #1204: memmove is wrong here: when comp_offset < comp_bytes the
// source and destination overlap and newly-written bytes must be read
// back (LZ77 run-length extension). memmove copies from original bytes.
for (; pos < end; pos++)
{
#ifndef NDEBUG
assert ((long)pos >= (long)comp_offset);
assert (pos - comp_offset < dec->size);
assert (pos < dec->size);
#endif
dec->chain[pos] = dec->chain[pos - comp_offset];
}
dec->byte = end;
// copy "literal data"
lit_length = opcode1 & 3;
if (lit_length == 0)
{
opcode1 = bit_read_RC (src);
LOG_INSANE ("size)
{
opcode1 = copy_bytes (lit_length, src, dec);
LOG_INSANE ("= DWG_LOGLEVEL_INSANE)
{
static int ctr = 0;
char out[80];
FILE *fp;
snprintf (out, 80, "decomp_%u.bin", ctr++);
fp = fopen (out, "wb");
fwrite (dec->chain, 1, dec->size, fp);
fclose (fp);
}
#endif
return 0; // Success
}
// index is the Section Number in the section map
static Dwg_Section *
find_section (Dwg_Data *dwg, BITCODE_RLd idx)
{
BITCODE_BL i;
if (dwg->header.section == 0 || idx == 0)
return 0;
for (i = 0; i < dwg->header.num_sections; ++i)
{
if (dwg->header.section[i].number == idx)
return &dwg->header.section[i];
}
return NULL;
}
static int
add_section (Dwg_Data *dwg)
{
if (dwg->header.num_sections == 0)
{
dwg->header.section = (Dwg_Section *)calloc (1, sizeof (Dwg_Section));
}
else
{
dwg->header.section = (Dwg_Section *)realloc (
dwg->header.section,
sizeof (Dwg_Section) * (dwg->header.num_sections + 1));
memset (&dwg->header.section[dwg->header.num_sections], 0,
sizeof (Dwg_Section));
}
if (!dwg->header.section)
{
LOG_ERROR ("Out of memory");
return DWG_ERR_OUTOFMEM;
}
dwg->header.num_sections++;
return 0;
}
// needed for r2004+ encode and decode (check-only)
// p 4.3: first calc the header with seed 0 and skipped checksum (as 0),
// then compress, then calc the compressed body with prev. checksum as seed.
// Does not advance dat->byte.
uint32_t
dwg_section_page_checksum (const uint32_t seed, Bit_Chain *restrict dat,
int32_t size, bool skip_checksum)
{
uint32_t sum1 = seed & 0xffff;
uint32_t sum2 = seed >> 0x10;
unsigned char *data = &dat->chain[dat->byte];
// only for skip_checksum
unsigned char *before = &dat->chain[dat->byte + 16];
unsigned char *after = &dat->chain[dat->byte + 20];
unsigned char *end = &dat->chain[dat->byte + size];
if (dat->byte + size > dat->size)
{
LOG_ERROR ("dwg_section_page_checksum size %" PRId32 " overflow", size);
return 0;
}
while (size > 0 && data < end)
{
uint32_t chunksize = MIN (size, 0x15b0);
size -= chunksize;
for (uint32_t i = 0; i < chunksize; i++)
{
if (!skip_checksum || data < before || data >= after)
sum1 += *data;
// else assume 0 for the existing checksum. mask it out
sum2 += sum1;
data++;
}
sum1 %= 0xFFF1;
sum2 %= 0xFFF1;
}
return (sum2 << 0x10) | (sum1 & 0xffff);
}
/* Read R2004, 2010+ Section Map
* The Section Map is a vector of number, size, and address(offset) triples
* used to locate the sections in the file.
*/
static int
read_R2004_section_map (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
BITCODE_RC *ptr;
BITCODE_RLL section_address;
long bytes_remaining;
int i, error = 0, found_section_map_id = 0;
Bit_Chain sec = *dat;
Bit_Chain dec = { 0 };
const BITCODE_RLd section_array_size
= dwg->fhdr.r2004_header.section_array_size;
const BITCODE_RLL section_map_address
= dwg->fhdr.r2004_header.section_map_address + 0x100;
const BITCODE_RLd section_map_id = dwg->fhdr.r2004_header.section_map_id;
BITCODE_RLd max_id = 0;
sec.size = dwg->fhdr.r2004_header.comp_data_size + dat->byte;
dec.size = dwg->fhdr.r2004_header.decomp_data_size;
dec.from_version = dec.version = dat->from_version;
dwg->header.num_sections = 0;
dwg->header.section = 0;
// decompressed data
if (dec.size > 0xff000000 || // 4Gb. max_decomp_size = 0x144400
sec.size > dat->size)
{
LOG_ERROR ("Invalid r2004_header.decomp_data_size %" PRIuSIZE, dec.size);
dwg->fhdr.r2004_header.decomp_data_size
= 8 * (BITCODE_RL)(sec.size & 0xffffffff);
return DWG_ERR_OUTOFMEM;
}
dec.chain = (BITCODE_RC *)calloc (dec.size + 1024, 1);
if (!dec.chain)
{
LOG_ERROR ("Out of memory");
return DWG_ERR_OUTOFMEM;
}
section_address = sec.byte;
error = decompress_R2004_section (&sec, &dec);
dat->byte = sec.byte;
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
LOG_INSANE ("dec: %" PRIuSIZE "\n", dec.size);
LOG_TRACE_TF (dec.chain, dec.size);
}
#endif
if (error > DWG_ERR_CRITICAL || error == DWG_ERR_VALUEOUTOFBOUNDS)
{
free (dec.chain);
return error;
}
LOG_TRACE ("\n#### Read 2004 Section Page Map @%x ####\n",
(unsigned)section_map_address);
section_address = 0x100; // starting address
i = 0;
bytes_remaining = (long)dec.size;
ptr = dec.chain;
dwg->header.num_sections = 0;
while (bytes_remaining >= 8)
{
error |= add_section (dwg);
if (error > DWG_ERR_CRITICAL)
return error;
// only the first two fields: number, size
bfr_read_32 (&dwg->header.section[i], &ptr, 8);
bytes_remaining -= 8;
LOG_TRACE ("Section[%2d]=%2d,", i, (int)dwg->header.section[i].number);
LOG_TRACE (" size: %5u,", (unsigned)dwg->header.section[i].size);
dwg->header.section[i].address = section_address;
if (dwg->header.section[i].number <= section_array_size) // GH #144
{
section_address += dwg->header.section[i].size;
LOG_TRACE (" address: 0x%04lx\n",
(unsigned long)dwg->header.section[i].address);
}
else
LOG_TRACE (" (ignored > %d section_array_size)\n",
(int)section_array_size);
if (dwg->header.section[i].number > max_id)
max_id = dwg->header.section[i].number;
// repair section_map_id.address from section_map_address
if (dwg->header.section[i].number == section_map_id)
{
found_section_map_id++;
if (dwg->header.section[i].address != section_map_address)
{
LOG_WARN ("Repair invalid section_map_address: %" PRIx64
" != %" PRIx64,
dwg->header.section[i].address, section_map_address);
error |= DWG_ERR_VALUEOUTOFBOUNDS;
dwg->header.section[i].address = section_map_address;
}
}
if (i >= (int)section_array_size)
{
error |= DWG_ERR_VALUEOUTOFBOUNDS;
LOG_WARN ("Overflow section_array_size: %d >= %d (remaining: %ld)",
i, (int)section_array_size, (long)bytes_remaining);
if (i > 1000)
return error;
}
if (bytes_remaining >= 16
&& dwg->header.section[i].number < 0) // negative: gap/unused data
//|| dwg->header.section[i].number > section_array_size))
{
bfr_read_32 (&dwg->header.section[i].parent, &ptr, 16);
bytes_remaining -= 16;
LOG_TRACE (" Parent: %d, ", dwg->header.section[i].parent);
LOG_TRACE ("Left: %d, ", dwg->header.section[i].left);
LOG_TRACE ("Right: %d, ", dwg->header.section[i].right);
LOG_TRACE ("0x00: %d\n", dwg->header.section[i].x00);
}
i++;
}
i--;
free (dec.chain);
if (max_id != section_array_size)
{
LOG_WARN ("Invalid section_array_size: [%u].%u != %u", i, max_id,
(unsigned)section_array_size);
}
if (section_address != dwg->fhdr.r2004_header.last_section_address + 0x100)
{
error |= DWG_ERR_VALUEOUTOFBOUNDS;
LOG_WARN ("Invalid last_section_address: %" PRIx64 " != %" PRIx64,
section_address, dwg->fhdr.r2004_header.last_section_address);
}
if (dwg->header.num_sections
!= dwg->fhdr.r2004_header.numgaps + dwg->fhdr.r2004_header.numsections)
{
error |= DWG_ERR_VALUEOUTOFBOUNDS;
LOG_WARN ("Invalid sections: %d != numgaps: " FORMAT_RL
" + numsections: " FORMAT_RL,
dwg->header.num_sections, dwg->fhdr.r2004_header.numgaps,
dwg->fhdr.r2004_header.numsections);
}
if (!found_section_map_id)
{
BITCODE_RLx section_type;
Dwg_Section *info;
LOG_WARN ("section_map_id %d not found", (int)section_map_id);
info = find_section (dwg, section_map_id);
if (!info)
{
i = dwg->header.num_sections;
add_section (dwg);
LOG_WARN ("Add section_map_id [%d] %d => address 0x%" PRIx64, i,
section_map_id, section_map_address);
error |= DWG_ERR_VALUEOUTOFBOUNDS;
dwg->header.section[i].number = section_map_id;
dwg->header.section[i].address = section_map_address;
if (i > 0)
dwg->header.section[i].size
= section_map_address - dwg->header.section[i - 1].address;
}
info = find_section (dwg, dwg->fhdr.r2004_header.section_info_id);
if (!info)
goto repair_info_id;
dat->bit = 0;
dat->byte = info->address;
section_type = bit_read_RL (dat);
if (section_type != 0x4163003b)
{
repair_info_id:
LOG_WARN ("Repair invalid section_info_id [%d]: => %d", i - 1,
(int)dwg->fhdr.r2004_header.section_info_id);
error |= DWG_ERR_VALUEOUTOFBOUNDS;
for (i = 0; i < (int)dwg->header.num_sections; ++i)
{
dat->bit = 0;
dat->byte = dwg->header.section[i].address;
if (dat->byte > dat->size)
break;
section_type = bit_read_RL (dat);
if (section_type == 0x4163003b)
{
LOG_WARN ("Fixed section_info_id [%d]: => %d @" FORMAT_RLL,
i, (int)dwg->fhdr.r2004_header.section_info_id,
dwg->header.section[i].address);
if (!info)
info = &dwg->header.section[i];
info->address = dwg->header.section[i].address;
info->size = dwg->header.section[i].size;
info->number = dwg->fhdr.r2004_header.section_info_id;
}
}
if (!info || info->number != dwg->fhdr.r2004_header.section_info_id)
{
i = dwg->header.num_sections;
add_section (dwg);
error |= DWG_ERR_VALUEOUTOFBOUNDS;
dwg->header.section[i].number
= dwg->fhdr.r2004_header.section_info_id;
dwg->header.section[i].address
= dwg->header.section[i - 1].address
+ dwg->header.section[i - 1].size;
LOG_WARN ("Add section_info_id [%d] %d => address 0x%" PRIx64, i,
dwg->fhdr.r2004_header.section_info_id,
dwg->header.section[i].address);
}
}
}
if (found_section_map_id > 1)
{
error |= DWG_ERR_VALUEOUTOFBOUNDS;
LOG_WARN (
"Illegal 2004 Section Page Map. Found %d section_map_id sections",
found_section_map_id);
}
return error;
}
/* R2004+ only */
unsigned int
section_max_decomp_size (const Dwg_Data *dwg, const Dwg_Section_Type id)
{
unsigned max_decomp_size = 0x7400;
if (id == SECTION_APPINFOHISTORY)
max_decomp_size = 0x1000;
else if (id == SECTION_APPINFO)
max_decomp_size = 0x400; // max seen 0x380
else if (id == SECTION_PREVIEW)
{
// resp. 0x1800 with r2013+, 0x4a000 with r2007-r2010
max_decomp_size = 0x7fff; // was 0x7c00. GH #1045
// if (dwg->header.version >= R_2013)
// max_decomp_size = 0x1800;
// else
if (dwg->header.version >= R_2007 && dwg->header.version <= R_2010)
max_decomp_size = 0x4a000;
else if (dwg->header.version == R_2004)
max_decomp_size = 0x144400;
}
else if (id == SECTION_SUMMARYINFO)
max_decomp_size = 0x100;
return max_decomp_size;
}
/* Read R2004, 2010+ Section Info, aka 2004 Data section map
*/
static int
read_R2004_section_info (Bit_Chain *restrict dat, Dwg_Data *restrict dwg,
uint32_t comp_data_size, uint32_t decomp_data_size)
{
Bit_Chain dec = { 0 };
BITCODE_BL i, j;
int error;
if (decomp_data_size > 0x2f000000 && // 790Mb
((uint64_t)decomp_data_size > 8 * (uint64_t)comp_data_size
|| comp_data_size > dat->size))
{
LOG_ERROR ("Invalid r2004_header.decomp_data_size %" PRIu32,
decomp_data_size);
return DWG_ERR_OUTOFMEM;
}
dec.size = decomp_data_size;
dec.chain = (BITCODE_RC *)calloc ((size_t)decomp_data_size + 1024,
sizeof (BITCODE_RC));
if (!dec.chain)
{
LOG_ERROR ("Out of memory");
return DWG_ERR_OUTOFMEM;
}
error = decompress_R2004_section (dat, &dec);
if (error > DWG_ERR_CRITICAL || error == DWG_ERR_VALUEOUTOFBOUNDS)
{
free (dec.chain);
return error;
}
dec.byte = 0;
LOG_TRACE ("\n#### Read 2004 section_infohdr ####\n");
{
Dwg_Object *obj = NULL;
Dwg_Section_InfoHdr *_obj = &dwg->header.section_infohdr;
dat = &dec;
FIELD_RL (num_desc, 0);
FIELD_RL (compressed, 0);
FIELD_RLx (max_size, 0);
FIELD_RL (encrypted, 0);
FIELD_RL (num_desc2, 0);
// dwg->header.section_infohdr.num_desc = bit_read_RL (&dec);
// LOG_TRACE ("num_desc: %d\n", dwg->header.section_infohdr.num_desc);
// dwg->header.section_infohdr.compressed = bit_read_RL (&dec);
// LOG_TRACE ("compressed: %d\n", dwg->header.section_infohdr.compressed);
// dwg->header.section_infohdr.max_size = bit_read_RL (&dec);
// LOG_TRACE ("max_size: 0x%x\n", dwg->header.section_infohdr.max_size);
// dwg->header.section_infohdr.encrypted = bit_read_RL (&dec);
// LOG_TRACE ("encrypted: %d\n", dwg->header.section_infohdr.encrypted);
// dwg->header.section_infohdr.num_desc2 = bit_read_RL (&dec);
// LOG_TRACE ("num_desc2: %d/0x%x\n",
// dwg->header.section_infohdr.num_desc2,
// dwg->header.section_infohdr.num_desc2)
}
assert (dec.byte == 20);
if (dwg->header.section_infohdr.num_desc
> 0xc0000000 / sizeof (Dwg_Section_Info))
{
LOG_ERROR ("Illegal num_desc");
free (dec.chain);
dwg->header.section_infohdr.num_desc = 0;
dwg->header.section_infohdr.num_desc2 = 0;
return error | DWG_ERR_INVALIDDWG;
}
dwg->header.section_info = (Dwg_Section_Info *)calloc (
dwg->header.section_infohdr.num_desc, sizeof (Dwg_Section_Info));
if (!dwg->header.section_info)
{
LOG_ERROR ("Out of memory");
return error | DWG_ERR_OUTOFMEM;
}
// decomp_end = decomp + decomp_data_size + 1024;
for (i = 0; i < dwg->header.section_infohdr.num_desc; ++i)
{
Dwg_Section_Info *info;
// uint64_t sum_decomp = 0;
uint64_t prev_address = 0;
unsigned max_decomp_size;
if (dec.byte + 8 + 6 * 4 + 64 >= dec.size)
{
free (dec.chain);
dwg->header.section_infohdr.num_desc = i;
LOG_ERROR ("read_R2004_section_info out of range");
return DWG_ERR_INVALIDDWG;
}
{
Dwg_Object *obj = NULL;
Dwg_Section_Info *_obj = &dwg->header.section_info[i];
info = _obj;
LOG_TRACE ("\nsection_info[%d] fields:\n", i);
FIELD_RLL (size, 0);
FIELD_RL (num_sections, 0);
FIELD_RLx (max_decomp_size, 0); // normally 0x7400, max 0x8000
FIELD_RL (unknown, 0);
FIELD_RL (compressed, 0); // 1=no, 2=yes
FIELD_RL (type, 0);
FIELD_RL (encrypted, 0); // 0=no, 1=yes, 2=unknown
bit_read_fixed (&dec, (BITCODE_RC *)info->name, 64);
LOG_TRACE ("name: \"%s\"\n", info->name);
// FIELD_TFF (name, 64, 0);
info->fixedtype = dwg_section_type (info->name);
LOG_TRACE ("fixedtype: %d\n\n", info->fixedtype);
}
// if (dec.byte >= dec.size)
// {
// info->name[0] = '\0';
// info->num_sections = 0;
// info->sections = NULL;
// dwg->header.section_infohdr.num_desc = i;
// free (dec.chain);
// LOG_ERROR ("read_R2004_section_info out of range");
// return DWG_ERR_INVALIDDWG;
// }
// max_decomp_size is the decompressed block size
max_decomp_size = section_max_decomp_size (dwg, info->fixedtype);
if (info->max_decomp_size > max_decomp_size)
{
LOG_ERROR ("Skip section %s with max decompression size 0x%x > 0x%x",
info->name, info->max_decomp_size, max_decomp_size);
info->max_decomp_size = info->size = 0;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
}
if (info->num_sections < 1000000)
{
// bug in Teigha with Template, with num_sections=0
/*
int32_t old_section_number = 0;
if (info->num_sections == 0
&& info->fixedtype == SECTION_TEMPLATE
// && is_teigha
&& info->size >= 4)
{
LOG_INFO ("Fixup TEMPLATE.num_sections to 1 (Teigha bug)\n");
info->num_sections = 1;
}
*/
/*
if (info->size > (int64_t)info->num_sections
* (int64_t)info->max_decomp_size * 2L)
{
LOG_ERROR ("Skip section %s with size %" PRId64 " > " FORMAT_RL
" * " FORMAT_RL,
info->name, info->size, info->num_sections,
info->max_decomp_size);
info->max_decomp_size = info->size = info->num_sections = 0;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
}
*/
if (info->num_sections > 1 && info->size < info->max_decomp_size)
{
// on mult. blocks, size must exceed the size of the first block
LOG_ERROR ("Skip section %s(%u) with size %" PRId64
" < max_decomp_size " FORMAT_RL,
info->name, info->type, info->size,
info->max_decomp_size);
info->max_decomp_size = info->size = info->num_sections = 0;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
}
LOG_INFO ("Page count %u in area %d\n", info->num_sections, i);
free (info->sections);
info->sections = (Dwg_Section **)calloc (info->num_sections,
sizeof (Dwg_Section *));
if (!info->sections)
{
free (dec.chain);
LOG_ERROR ("Out of memory with %u sections", info->num_sections);
return error | DWG_ERR_OUTOFMEM;
}
prev_address = 0;
for (j = 0; j < info->num_sections; j++)
{
struct _section_page
{
int32_t number;
uint32_t size;
uint64_t address;
} page;
if (dec.byte + 16 > dec.size)
{
LOG_ERROR ("read_R2004_section_info[%u] out of range "
"%zu/%zu, abort",
j, dec.byte, dec.size);
info->num_sections = j;
error |= DWG_ERR_SECTIONNOTFOUND;
break;
}
/* endian specific code: */
page.number = bit_read_RL (&dec);
page.size = bit_read_RL (&dec);
page.address = bit_read_RLL (&dec);
// sum_decomp += page.size; /* TODO: uncompressed size */
#if 0
if (page.address < sum_decomp)
{
/* ODA: "If the start offset is smaller than the sum of the decompressed
* size of all previous pages, then this page is to be preceded by
* zero pages until this condition is met. */
LOG_WARN ("address %lu < sum_decomp %lu", page.address, sum_decomp);
}
#endif
info->sections[j] = find_section (dwg, page.number);
if (info->sections[j])
{
LOG_HANDLE (" section[%d].info[%d]: %s type %d => ", i,
j, info->name, info->sections[j]->type);
info->sections[j]->type = info->fixedtype;
LOG_HANDLE ("type %d\n", info->sections[j]->type);
}
if (page.number < 0)
{ // gap/unused data
LOG_TRACE ("Page: %4" PRId32 " (-)", page.number);
info->num_sections++;
info->sections = (Dwg_Section **)realloc (
info->sections,
info->num_sections * sizeof (Dwg_Section *));
info->sections[info->num_sections - 1] = NULL;
}
else if (page.address < prev_address)
{
LOG_TRACE ("Page: %4" PRId32 " (a)", page.number);
}
else if (info->sections[0]
&& page.number > (int32_t)(info->num_sections
+ info->sections[0]->number))
{
// for [7] ptr+160 seems to be AcDb:ObjFreeSpace
LOG_TRACE ("Page: %4" PRId32 " (n)", page.number);
}
/*
else if (!info->sections[j]
&& page.number != old_section_number + 1)
{
LOG_WARN ("Page: %4" PRId32 " (b)", page.number);
LOG_TRACE (" size: %5" PRIu32, page.size) // compressed
LOG_TRACE (" address: 0x%" PRIx64, page.address);
if (info->sections[j])
LOG_TRACE (" info: 0x%" PRIx64,
info->sections[j]->address);
LOG_TRACE ("\n");
dec.byte -= 16;
break;
}
*/
else
{
LOG_TRACE ("Page: %4" PRId32 " ", page.number);
// old_section_number = page.number;
prev_address = page.address;
}
LOG_TRACE (" size: %5" PRIu32, page.size); // compressed
LOG_TRACE (" address: 0x%" PRIx64, page.address);
if (info->sections[j])
LOG_TRACE (" info: 0x%" PRIx64, info->sections[j]->address);
LOG_TRACE ("\n");
}
}
else
{
LOG_ERROR ("Section count %u in area %d too high! Skipping",
info->num_sections, i);
info->num_sections = 0;
free (dec.chain);
return error | DWG_ERR_VALUEOUTOFBOUNDS;
}
}
free (dec.chain);
return error;
}
/* Encrypted Section Header */
#pragma pack(push)
#pragma pack(1)
typedef union _encrypted_section_header
{
uint32_t long_data[8];
struct
{
uint32_t page_type; // always 0x4163043b
uint32_t section_type; // see dwg_section_type()
uint32_t data_size; // compressed
uint32_t page_size; // decompressed
uint32_t address; // start offset into page_size
uint32_t unknown; // oda writes 0
uint32_t page_header_crc; // section page checksum from unencoded header
uint32_t data_crc; // from compressed data
} fields;
} encrypted_section_header;
#pragma pack(pop)
static int
read_2004_compressed_section (Bit_Chain *dat, Dwg_Data *restrict dwg,
Bit_Chain *sec_dat, Dwg_Section_Type type)
{
uint32_t max_decomp_size;
long bytes_left;
Dwg_Section_Info *info = NULL;
encrypted_section_header es;
Bit_Chain dec = { 0 };
BITCODE_BL i, j;
int error = 0;
for (i = 0; i < dwg->header.section_infohdr.num_desc && !info; ++i)
{
if (dwg->header.section_info[i].fixedtype == type)
{
info = &dwg->header.section_info[i];
break;
}
}
if (!info)
{
if (type < SECTION_REVHISTORY && type != SECTION_TEMPLATE
&& type != SECTION_OBJFREESPACE)
{
LOG_WARN ("Failed to find section_info[%u] with type %d", i, type);
return DWG_ERR_SECTIONNOTFOUND;
}
else
{
LOG_TRACE ("Found no section_info[%u] with type %d\n", i, type);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
}
else
{
LOG_TRACE ("\nFound section_info[" FORMAT_BL
"] %s type %d with %d sections (%scompressed):\n",
i, info->name, type, info->num_sections,
info->compressed == 2 ? "" : "un");
}
if (info->num_sections == 0)
{
// XXX: This Teigha bug is already fixed up before
if (type == SECTION_TEMPLATE && is_teigha && info->size >= 4
&& info->unknown == 1)
{
// bug in Teigha with Template, with num_sections=0
BITCODE_BL sec_idx;
info->num_sections = 1;
info->sections = realloc (info->sections, sizeof (Dwg_Section *));
if (!info->sections)
return DWG_ERR_OUTOFMEM;
// use a real section, so that dwg_free releases it together with
// dwg->header.section and no orphan remains
sec_idx = dwg->header.num_sections;
if (add_section (dwg) != 0)
return DWG_ERR_OUTOFMEM;
info->sections[0] = &dwg->header.section[sec_idx];
}
/*
else if (type == SECTION_UNKNOWN)
{
Dwg_Section *sec = calloc (1, sizeof (Dwg_Section));
info->num_sections = 1; // hidden. enable it
info->size = 128;
sec->type = SECTION_UNKNOWN;
sec->compression_type = info->compressed;
sec->address = 128;
sec->size = 128;
if (info->sections) // [0] is NULL anyway
free (info->sections);
info->sections = calloc (1, sizeof (Dwg_Section*));
info->sections[0] = sec;
}
*/
else
return 0;
}
if (!info->sections)
{
LOG_ERROR ("Empty sections for %s", info->name);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
// Compute in 64-bit: both factors are uint32 (BITCODE_RL), so the product
// overflows the uint32_t max_decomp_size and wraps to a small value, letting
// the guard below pass while info->size (checked as int64 at :2059) stays
// large -> under-allocation and a later OOB sentinel scan. GH security.
{
uint64_t max_decomp_size64
= (uint64_t)info->num_sections * (uint64_t)info->max_decomp_size;
if (max_decomp_size64 == 0 || max_decomp_size64 > 0x2f000000) // 790Mb
{
LOG_ERROR ("Invalid section %s count or max decompression size. "
"Sections: " FORMAT_RL ", Max size: " FORMAT_RL,
info->name, info->num_sections, info->max_decomp_size);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
max_decomp_size = (uint32_t)max_decomp_size64;
}
if (info->size > (int64_t)info->num_sections * (int64_t)info->max_decomp_size
|| info->size < 0)
{
if (info->size < 0 || info->size > 0x2f000000) // 790Mb
{
LOG_ERROR ("Invalid section %s size %" PRId64 " > %u * " FORMAT_RL,
info->name, info->size, info->num_sections,
info->max_decomp_size);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
/* Seen with large AcDb:AcDsPrototype_1b (ACIS datastorage) sections:
the pages are bigger than max_decomp_size, so the total size exceeds
num_sections * max_decomp_size. Allocate the real size instead of
rejecting the section. The per-page bounds checks below still use
max_decomp_size, i.e. the allocation. Add one page of headroom,
a page at the end may start below info->size and be checked with
the full page size. */
LOG_WARN ("Section %s size %" PRId64 " > %u * " FORMAT_RL, info->name,
info->size, info->num_sections, info->max_decomp_size);
max_decomp_size = (uint32_t)info->size + info->max_decomp_size;
}
LOG_HANDLE ("Alloc section %s size %" PRIu32 "\n", info->name,
max_decomp_size);
dec.opts = dwg->opts & DWG_OPTS_LOGLEVEL;
dec.size = max_decomp_size;
dec.version = dat->version;
dec.from_version = dat->from_version;
dec.chain = (BITCODE_RC *)calloc (max_decomp_size, 1);
if (!dec.chain)
{
LOG_ERROR ("Out of memory with " FORMAT_RL
" sections of size: " FORMAT_RL,
info->num_sections, info->max_decomp_size);
return DWG_ERR_OUTOFMEM;
}
bytes_left = max_decomp_size;
sec_dat->bit = 0;
sec_dat->byte = 0;
sec_dat->version = dat->version;
sec_dat->from_version = dat->from_version;
sec_dat->chain = dec.chain;
sec_dat->size = 0;
for (i = j = 0; i < info->num_sections; ++i, ++j)
{
uint64_t address; // the section map keeps it 64bit, do not truncate
uint32_t sec_mask;
if (!info->sections[i])
{
LOG_WARN ("Skip empty section %u %s", i, info->name);
if (i == info->num_sections - 1) // the last one
{
free (dec.chain);
sec_dat->chain = NULL;
return DWG_ERR_SECTIONNOTFOUND;
}
j--; // index for writing info->max_decomp_size chunks
continue;
}
address = info->sections[i]->address;
if (address > dat->size || dat->size - address < 32)
{
LOG_ERROR ("Section %s address %" PRIu64 " + 32 > %" PRIuSIZE,
info->name, address, dat->size);
free (dec.chain);
sec_dat->chain = NULL;
return DWG_ERR_VALUEOUTOFBOUNDS;
}
memcpy (es.long_data, &dat->chain[address], 32);
dat->byte = address + 32;
// bit_read_fixed (dat, es.char_data, 32);
//? if encrypted properties: security_type & 2 ??
sec_mask = htole32 (0x4164536b ^ (uint32_t)address);
{
for (int k = 0; k < 8; ++k)
es.long_data[k] = le32toh (es.long_data[k] ^ sec_mask);
}
LOG_INFO ("=== Section %s (%u) @%" PRIu64 " ===\n", info->name, i,
address);
if (es.fields.page_type != 0x4163043b)
LOG_WARN ("page_type: 0x%x (should be 0x4163043b)",
(unsigned)es.fields.page_type);
else
LOG_INFO ("page_type: 0x%x\n", (unsigned)es.fields.page_type);
LOG_INFO ("Section Type: %u\n", (unsigned)es.fields.section_type);
// this is the number of bytes that is read in decompress_R2004_section
// (+ 2bytes)
LOG_INFO ("Data size: 0x%x/%u (compressed)\n",
(unsigned)es.fields.data_size, (unsigned)es.fields.data_size);
LOG_INFO ("Page size: 0x%x/%u (decompressed)\n",
(unsigned)es.fields.page_size, (unsigned)es.fields.page_size);
LOG_TRACE ("StartOffset: 0x%x/%u\n", (unsigned)es.fields.address,
(unsigned)es.fields.address);
LOG_HANDLE ("Unknown: 0x%x\n", (unsigned)es.fields.unknown);
LOG_HANDLE ("page_header_crc: 0x%X\n",
(unsigned)es.fields.page_header_crc);
LOG_HANDLE ("data_crc: 0x%X\n", (unsigned)es.fields.data_crc);
LOG_TRACE ("Section start: %" PRIuSIZE "\n\n", dat->byte);
// GH #126 part 4
LOG_INSANE ("info[%d]->max_decomp_size: %" PRIu32 " (0x%" PRIx32 ")\n",
i, info->max_decomp_size, info->max_decomp_size);
LOG_INSANE ("max_decomp_size: %" PRIu32 " (0x%" PRIx32 ")\n",
max_decomp_size, max_decomp_size);
LOG_INSANE ("bytes_left: %ld\n", bytes_left);
// check if compressed at all
if (info->compressed == 2 && bytes_left > 0
&& es.fields.address <= max_decomp_size
&& (uint64_t)es.fields.address + info->max_decomp_size
<= max_decomp_size)
{
size_t orig_size = dat->size;
dec.byte = es.fields.address;
/* == j * info->max_decomp_size;*/ // offset
LOG_INSANE ("dec offset: %" PRIuSIZE "\n", dec.byte);
/* dec.size stays pinned to max_decomp_size (the true allocation).
Overwriting it here with a smaller per-section value causes
bit_chain_alloc to realloc the buffer to a smaller size, then
dec.byte and dec.size drift past the shrunk allocation resulting
in a heap-buffer-overflow WRITE (GHSA-qg2f-8389-w95j). The
pre-loop guard already ensures address+max_decomp_size <=
max_decomp_size, so the decompress bounds check is still valid. */
LOG_INSANE ("dec size: %" PRIuSIZE "\n", dec.size);
dat->size = dat->byte + es.fields.data_size;
if (dat->size > orig_size)
{
LOG_ERROR ("Compressed section data_size overflow");
dat->size = orig_size;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
if (type == SECTION_AUXHEADER)
{
FILE *fp = fopen ("comp_auxh.bin", "wb");
fwrite (&dat->chain[dat->byte], 1, es.fields.data_size, fp);
fclose (fp);
}
else if (type == SECTION_OBJFREESPACE)
{
FILE *fp = fopen ("comp_ofs.bin", "wb");
fwrite (&dat->chain[dat->byte], 1, es.fields.data_size, fp);
fclose (fp);
}
}
#endif
error = decompress_R2004_section (dat, &dec);
sec_dat->chain = dec.chain; // may be realloced
dat->size = orig_size;
if (error > DWG_ERR_CRITICAL)
{
free (dec.chain);
sec_dat->chain = NULL;
return error;
}
bytes_left -= info->max_decomp_size;
sec_dat->size += info->max_decomp_size;
}
else
{
const size_t offset = address + 32;
// 64 bit: address, size and page_size come from the file, their
// 32 bit sums wrap and pass the checks below
const uint64_t size
= es.fields.address > info->size
? es.fields.page_size
: MIN ((uint64_t)(info->size - es.fields.address),
(uint64_t)es.fields.page_size);
if (info->compressed == 2 || bytes_left < 0
|| es.fields.address > max_decomp_size
|| (uint64_t)es.fields.address + size > max_decomp_size
|| (uint64_t)es.fields.address + size > dec.size
|| (uint64_t)offset + size > dat->size)
{
LOG_ERROR ("Some section size or address out of bounds");
free (dec.chain);
sec_dat->chain = NULL;
return type < SECTION_REVHISTORY ? DWG_ERR_INVALIDDWG
: DWG_ERR_VALUEOUTOFBOUNDS;
}
assert (j < info->num_sections);
memcpy (&dec.chain[es.fields.address], &dat->chain[offset], size);
bytes_left -= size;
sec_dat->size += size;
}
}
sec_dat->size = info->size;
return error;
}
/* R2004, 2010+ Class Section
*/
static int
read_2004_section_classes (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
BITCODE_RL size;
BITCODE_BS max_num, i;
char c;
int error;
Bit_Chain sec_dat = { 0 }, str_dat = { 0 };
Dwg_Object *obj = NULL;
memset (&sec_dat, 0, sizeof (sec_dat));
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_CLASSES);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_ERROR ("Failed to read compressed %s section", "Classes");
if (sec_dat.chain)
free (sec_dat.chain);
return error;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_cls.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
if (bit_search_sentinel (&sec_dat, dwg_sentinel (DWG_SENTINEL_CLASS_BEGIN)))
{
BITCODE_RL bitsize = 0;
LOG_TRACE ("\nClasses\n-------------------\n");
size = bit_read_RL (&sec_dat); // size of class data area
LOG_TRACE ("size: " FORMAT_RL " [RL]\n", size);
if ((dat->from_version >= R_2010 && dwg->header.maint_version > 3)
|| dat->from_version >= R_2018)
{
BITCODE_RL hsize = bit_read_RL (&sec_dat);
LOG_TRACE ("hsize: " FORMAT_RL " [RL]\n", hsize);
}
if (dat->from_version >= R_2007)
{
bitsize = bit_read_RL (&sec_dat);
LOG_TRACE ("bitsize: " FORMAT_RL " [RL]\n", bitsize);
}
max_num = bit_read_BS (&sec_dat); // Maximum class number
LOG_TRACE ("max_num: " FORMAT_BS " [BS]\n", max_num);
// or just a single RS (always 0)
c = bit_read_RC (&sec_dat); // 0x00
LOG_HANDLE ("c: " FORMAT_RC " [RC]\n", c);
c = bit_read_RC (&sec_dat); // 0x00
LOG_HANDLE ("c: " FORMAT_RC " [RC]\n", c);
c = bit_read_B (&sec_dat); // 1
LOG_HANDLE ("b: " FORMAT_B " [B]\n", c);
dwg->layout_type = 0;
dwg->num_classes = max_num - 499;
if (max_num < 500
|| dwg->num_classes > 100 + (size / sizeof (Dwg_Class)))
{
LOG_ERROR ("Invalid max class number %d", (int)max_num);
dwg->num_classes = 0;
if (sec_dat.chain)
free (sec_dat.chain);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
assert (max_num >= 500);
if (dat->from_version >= R_2007)
{
memset (&str_dat, 0, sizeof (str_dat));
section_string_stream (dwg, &sec_dat, bitsize, &str_dat);
}
dwg->dwg_class
= (Dwg_Class *)calloc (dwg->num_classes, sizeof (Dwg_Class));
if (!dwg->dwg_class)
{
LOG_ERROR ("Out of memory");
if (sec_dat.chain)
free (sec_dat.chain);
return DWG_ERR_OUTOFMEM;
}
for (i = 0; i < dwg->num_classes; i++)
{
dwg->dwg_class[i].number = bit_read_BS (&sec_dat);
dwg->dwg_class[i].proxyflag = bit_read_BS (&sec_dat);
LOG_TRACE ("-------------------\n");
LOG_TRACE ("Number: %d [BS]\n", dwg->dwg_class[i].number);
LOG_TRACE ("Proxyflag: %x [BS]\n",
dwg->dwg_class[i].proxyflag);
dwg_log_proxyflag (DWG_LOGLEVEL, DWG_LOGLEVEL_TRACE,
dwg->dwg_class[i].proxyflag);
if (dwg->header.version >= R_2007)
{
unsigned int applen, dxflen;
dwg->dwg_class[i].appname
= (char *)bit_read_TU_len (&str_dat, &applen);
dwg->dwg_class[i].cppname = (char *)bit_read_TU (&str_dat);
dwg->dwg_class[i].dxfname_u
= bit_read_TU_len (&str_dat, &dxflen);
dwg->dwg_class[i].dxfname
= bit_TU_to_utf8_len (dwg->dwg_class[i].dxfname_u, dxflen);
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_TRACE)
{
char *appu8 = bit_TU_to_utf8_len (
(BITCODE_TU)dwg->dwg_class[i].appname, applen);
LOG_TRACE ("Application name: \"%s\" [TU 0 (as utf-8)]\n",
appu8);
// LOG_TRACE_TU ("Application name",
// dwg->dwg_class[i].appname, 0)
LOG_TRACE_TU ("C++ class name ", dwg->dwg_class[i].cppname,
0);
LOG_TRACE_TU ("DXF record name ",
dwg->dwg_class[i].dxfname_u, 0);
free (appu8);
}
}
else
{
dwg->dwg_class[i].appname = bit_read_TV (&sec_dat);
dwg->dwg_class[i].cppname = bit_read_TV (&sec_dat);
dwg->dwg_class[i].dxfname = bit_read_TV (&sec_dat);
LOG_TRACE ("Application name: %s [TV]\n",
dwg->dwg_class[i].appname);
LOG_TRACE ("C++ class name: %s [TV]\n",
dwg->dwg_class[i].cppname);
LOG_TRACE ("DXF record name: %s [TV]\n",
dwg->dwg_class[i].dxfname);
}
dwg->dwg_class[i].is_zombie
= bit_read_B (&sec_dat); // now called proxy
dwg->dwg_class[i].item_class_id = bit_read_BS (&sec_dat);
LOG_TRACE ("Class ID: 0x%x [BS] "
"(0x1f3 for object, 0x1f2 for entity)\n",
dwg->dwg_class[i].item_class_id);
dwg->dwg_class[i].num_instances = bit_read_BL (&sec_dat);
if (dat->from_version >= R_2004)
{
// spec says BS, but AutoCAD writes BL
dwg->dwg_class[i].dwg_version = bit_read_BL (&sec_dat);
dwg->dwg_class[i].maint_version = bit_read_BL (&sec_dat);
}
else
{
dwg->dwg_class[i].dwg_version = bit_read_BS (&sec_dat);
dwg->dwg_class[i].maint_version = bit_read_BS (&sec_dat);
}
dwg->dwg_class[i].unknown_1 = bit_read_BL (&sec_dat);
dwg->dwg_class[i].unknown_2 = bit_read_BL (&sec_dat);
LOG_TRACE ("num_instances: %u\n",
dwg->dwg_class[i].num_instances);
LOG_HANDLE ("dwg version: %u (%u)\n",
dwg->dwg_class[i].dwg_version,
dwg->dwg_class[i].maint_version);
LOG_HANDLE ("unknown: %u %u\n", dwg->dwg_class[i].unknown_1,
dwg->dwg_class[i].unknown_2);
if (dwg->dwg_class[i].dxfname
&& strEQ (dwg->dwg_class[i].dxfname, "LAYOUT"))
dwg->layout_type = dwg->dwg_class[i].number;
}
}
else
{
LOG_ERROR ("Failed to find class section sentinel");
free (sec_dat.chain);
return DWG_ERR_CLASSESNOTFOUND;
}
// then RS: CRC
// dwg_sentinel(DWG_SENTINEL_CLASS_END)
// SINCE(R_2004) 8 unknown bytes
free (sec_dat.chain);
return 0;
}
/* R2004, 2010+ Header Section
*/
static int
read_2004_section_header (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
int error;
Bit_Chain sec_dat = { 0 };
sec_dat.opts = dwg->opts & DWG_OPTS_LOGLEVEL;
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_HEADER);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_ERROR ("Failed to read compressed %s section", "Header");
if (sec_dat.chain)
free (sec_dat.chain);
return error;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_hea.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
if (dat->size - dat->byte <= 200)
{
LOG_ERROR ("Not enough space for HEADER %" PRIuSIZE,
dat->size - dat->byte);
return error | DWG_ERR_INVALIDDWG;
}
if (bit_search_sentinel (&sec_dat,
dwg_sentinel (DWG_SENTINEL_VARIABLE_BEGIN)))
{
LOG_TRACE ("\nHeader\n-------------------\n");
dwg->header_vars.size = bit_read_RL (&sec_dat);
LOG_TRACE ("size: " FORMAT_RL " [RL]\n", dwg->header_vars.size);
PRE (R_2007a)
{
error
|= dwg_decode_header_variables (&sec_dat, &sec_dat, &sec_dat, dwg);
}
else
{
Bit_Chain hdl_dat, str_dat;
BITCODE_RL endbits = 160; // start bit: 16 sentinel + 4 size
hdl_dat = sec_dat;
str_dat = sec_dat;
if (dwg->header.maint_version > 3 || dat->from_version >= R_2018)
{
dwg->header_vars.bitsize_hi = bit_read_RL (&sec_dat);
LOG_TRACE ("bitsize_hi: " FORMAT_RL " [RL]\n",
dwg->header_vars.bitsize_hi);
endbits += 32; // 192
}
dwg->header_vars.bitsize = bit_read_RL (&sec_dat);
LOG_TRACE ("bitsize: " FORMAT_RL " [RL]\n", dwg->header_vars.bitsize);
endbits += dwg->header_vars.bitsize;
bit_set_position (&hdl_dat, endbits);
section_string_stream (dwg, &sec_dat, dwg->header_vars.bitsize,
&str_dat);
error
|= dwg_decode_header_variables (&sec_dat, &hdl_dat, &str_dat, dwg);
}
}
free (sec_dat.chain);
return error;
}
/* R2004, 2010+ Handles Section
*/
static int
read_2004_section_handles (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain obj_dat = { 0 }, hdl_dat = { 0 };
BITCODE_RS section_size = 0;
size_t endpos;
int error;
obj_dat.opts = hdl_dat.opts = dwg->opts & DWG_OPTS_LOGLEVEL;
error = read_2004_compressed_section (dat, dwg, &obj_dat, SECTION_OBJECTS);
if (error >= DWG_ERR_CRITICAL || !obj_dat.chain)
{
LOG_ERROR ("Failed to read compressed %s section", "AcDbObjects");
if (obj_dat.chain)
free (obj_dat.chain);
return error;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_obj.bin", "wb");
fwrite (obj_dat.chain, 1, obj_dat.size, fp);
fclose (fp);
}
#endif
error = read_2004_compressed_section (dat, dwg, &hdl_dat, SECTION_HANDLES);
if (error >= DWG_ERR_CRITICAL || !hdl_dat.chain)
{
LOG_ERROR ("Failed to read compressed %s section", "Handles");
free (obj_dat.chain);
if (hdl_dat.chain)
free (hdl_dat.chain);
return error;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_hdl.bin", "wb");
fwrite (hdl_dat.chain, 1, hdl_dat.size, fp);
fclose (fp);
}
#endif
endpos = hdl_dat.byte + hdl_dat.size;
dwg->num_objects = 0;
do
{
size_t last_offset;
// BITCODE_RLL last_handle;
size_t oldpos = 0;
size_t startpos = hdl_dat.byte;
size_t max_handles = hdl_dat.size * 2;
uint16_t crc1, crc2;
section_size = bit_read_RS_BE (&hdl_dat);
LOG_TRACE ("\nHandles page size: %u [RS_BE]\n", section_size);
/* ***********************************************
* ODA p. 251 "Note that each section is cut off at a maximum length of
* 2032." BUT in fact files exist with 2036 section size */
if (section_size > 2040)
{
LOG_ERROR ("Object-map/handles page size greater than 2040!");
if (hdl_dat.chain)
free (hdl_dat.chain);
if (obj_dat.chain)
free (obj_dat.chain);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
last_offset = 0;
while ((long)(hdl_dat.byte - startpos) < (long)section_size)
{
int added;
BITCODE_UMC handleoff;
BITCODE_MC offset;
BITCODE_RLL last_handle
= dwg->num_objects
? dwg->object[dwg->num_objects - 1].handle.value
: 0;
BITCODE_MC prevsize
= dwg->num_objects ? dwg->object[dwg->num_objects - 1].size + 4
: 0;
oldpos = hdl_dat.byte;
// the offset from the previous handle. default: 1, unsigned
handleoff = bit_read_UMC (&hdl_dat);
// A failed modular char read (nine or more high-bit bytes in a row)
// derails the stream; resync one byte later instead of trusting the
// garbage. Only on !handleoff: the other conditions below also fire
// on entries that parsed fine, and resyncing on them costs good
// entries (Leader.dwg would lose all its modelspace entities).
if (!handleoff)
{
hdl_dat.byte = oldpos + 1;
handleoff = bit_read_UMC (&hdl_dat);
}
// the offset from the previous address. default: obj->size
offset = bit_read_MC (&hdl_dat);
if (!handleoff || handleoff > max_handles - last_handle
|| (offset > -4 && offset < prevsize))
{
LOG_WARN ("Ignore invalid handleoff (@%" PRIuSIZE ")", oldpos);
if (offset == 1
|| (offset > 0 && offset < prevsize && prevsize > 0)
|| (offset < 0 && labs ((long)offset) < prevsize
&& prevsize > 0))
{
if (offset != prevsize)
LOG_WARN ("offset " FORMAT_MC
" looks wrong, should be prevsize " FORMAT_MC
" + 4",
offset, prevsize - 4);
// handleoff = 1;
// offset = prevsize;
// LOG_WARN ("Recover invalid handleoff to %" PRIuSIZE " and
// offset to %ld",
// handleoff, offset);
}
}
last_offset += offset;
LOG_TRACE ("\n< Next object: %lu ", (unsigned long)dwg->num_objects);
LOG_HANDLE ("Handleoff: " FORMAT_UMC " [UMC] "
"Offset: " FORMAT_MC " [MC] @%" PRIuSIZE "\n",
handleoff, offset, last_offset);
if (hdl_dat.byte == oldpos) // ?? completely unrelated
break;
added = dwg_decode_add_object (dwg, &obj_dat, &obj_dat, last_offset);
if (added > 0)
error |= added;
// else re-allocated
// we don't stop encoding on single errors, but we sum them all up
// as combined bitmask
}
if (hdl_dat.byte == oldpos)
break;
#if 0
if (!bit_check_CRC(&hdl_dat, startpos, 0xC0C1))
error |= DWG_ERR_WRONGCRC;
//LOG_WARN("Handles section CRC mismatch at offset %zx", startpos);
#else
crc1 = bit_calc_CRC (0xC0C1, &(hdl_dat.chain[startpos]),
hdl_dat.byte - startpos);
crc2 = bit_read_RS_BE (&hdl_dat);
LOG_TRACE ("Handles page crc: %04X [RS_BE]\n", crc2);
if (crc1 == crc2)
{
LOG_INSANE ("Handles page CRC: %04X from %" PRIuSIZE "-%" PRIuSIZE
"=%ld\n",
crc2, startpos, hdl_dat.byte - 2,
(long)(hdl_dat.byte - startpos - 2));
}
else
{
LOG_WARN ("Handles page CRC mismatch: %04X vs calc. %04X from "
"%" PRIuSIZE "-%" PRIuSIZE "=%ld\n",
crc2, crc1, startpos, hdl_dat.byte - 2,
(long)(hdl_dat.byte - startpos - 2));
error |= DWG_ERR_WRONGCRC;
}
#endif
if (hdl_dat.byte >= endpos)
break;
}
while (section_size > 2);
free (hdl_dat.chain);
free (obj_dat.chain);
return error;
}
// may return OUTOFBOUNDS, needs to free the chain then
static int
summaryinfo_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_SummaryInfo *_obj = &dwg->summaryinfo;
Dwg_Object *obj = NULL;
int error = 0;
// clang-format off
#include "summaryinfo.spec"
// clang-format on
return error;
}
/* R2004, 2010+ SummaryInfo Section
*/
static int
read_2004_section_summary (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
// not compressed, page size: 0x100
int error = read_2004_compressed_section (dat, dwg, &sec_dat,
SECTION_SUMMARYINFO); // always 9
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_ERROR ("Failed to read uncompressed %s section", "SummaryInfo");
if (sec_dat.chain)
free (sec_dat.chain);
return error;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_si.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
if (dwg->header.summaryinfo_address != (BITCODE_RL)dat->byte)
LOG_WARN ("summaryinfo_address mismatch: " FORMAT_RL " != %" PRIuSIZE,
dwg->header.summaryinfo_address, dat->byte);
LOG_TRACE ("SummaryInfo (%" PRIuSIZE ")\n-------------------\n",
sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error = summaryinfo_private (dat, dwg);
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
// may return OUTOFBOUNDS, needs to free the chain then
static int
auxheader_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_AuxHeader *_obj = &dwg->auxheader;
Dwg_Object *obj = NULL;
int error = 0;
BITCODE_BL vcount;
if (!dat->chain || !dat->size)
return 1;
// clang-format off
#include "auxheader.spec"
// clang-format on
return error;
}
// r13 - r2000
static int
secondheader_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_SecondHeader *_obj = &dwg->secondheader;
// for error logging only:
#ifndef __cplusplus
Dwg_Object *obj = &(Dwg_Object){ .name = (char *)"2NDHEADER" };
#else
Dwg_Object xobj;
xobj.name = (char *)"2NDHEADER";
Dwg_Object *obj = &xobj;
#endif
int error = 0;
BITCODE_BL vcount;
if (!dat->chain || !dat->size)
return 1;
// clang-format off
#include "2ndheader.spec"
// Compute handle values from raw big-endian bytes (reverse of bit_H_to_dat)
for (unsigned i = 0; i < _obj->num_handles; i++)
{
Dwg_SecondHeader_Handles *h = &_obj->handles[i];
if (h->num_hdl <= 8)
{
h->value = 0;
for (int k = 0; k < h->num_hdl; k++)
h->value = (h->value << 8) | h->hdl[k];
}
LOG_TRACE ("[%u] %s: 0.%hu." FORMAT_HV "\n", (unsigned)i,
h->name ? h->name : "", h->num_hdl, h->value);
}
// clang-format on
if (!bit_check_CRC (dat, _obj->address + 16, 0xC0C1))
error |= DWG_ERR_WRONGCRC;
DEBUG_HERE
VERSIONS (R_14, R_2000)
{
FIELD_RLL (junk_r14, 0);
}
return error;
}
static int
objfreespace_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
// Bit_Chain *str_dat = dat;
Dwg_ObjFreeSpace *_obj = &dwg->objfreespace;
Dwg_Object *obj = NULL;
int error = 0;
// BITCODE_BL vcount;
if (!dat->chain || !dat->size)
return 1;
// clang-format off
#include "objfreespace.spec"
// clang-format on
return error;
}
/* R13c3+ AuxHeader Section
*/
static int
read_2004_section_auxheader (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
// type: 2, compressed, page size: 0x7400
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_AUXHEADER);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_ERROR ("Failed to read uncompressed %s section", "AuxHeader");
if (sec_dat.chain)
free (sec_dat.chain);
return error;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_auxh.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("AuxHeader (%" PRIuSIZE ")\n-------------------\n", sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error = auxheader_private (dat, dwg);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
// may return OUTOFBOUNDS, needs to free the chain then
// page size: 0x80, max seen max_decomp_size: 0x380
static int
appinfo_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_AppInfo *_obj = &dwg->appinfo;
Dwg_Object *obj = NULL;
int error = 0;
if (!dat->chain || !dat->size)
return 1;
// clang-format off
#include "appinfo.spec"
// clang-format on
if (_obj->version)
{
if (_obj->class_version > 2
&& bit_wcs2len ((BITCODE_TU)_obj->version) >= 6)
{
is_teigha = memcmp (_obj->version, "T\0e\0i\0g\0h\0a\0", 12) == 0;
LOG_TRACE ("is_teigha: %s\n", is_teigha ? "true" : "false");
}
else if (_obj->class_version == 2 && dwg->header.version < R_2007
&& strlen (_obj->version) >= 6)
{
is_teigha = memcmp (_obj->version, "Teigha", 6) == 0;
LOG_TRACE ("is_teigha: %s\n", is_teigha ? "true" : "false");
}
}
return error;
}
/* R2004, 2010+ AppInfo Section
*/
static int
read_2004_section_appinfo (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
Dwg_AppInfo *_obj = &dwg->appinfo;
// type: 0xc or 0xb
// not compressed, page size: 0x80
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_APPINFO);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", "AppInfo");
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_ai.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("AppInfo (%" PRIuSIZE ")\n-------------------\n", sec_dat.size);
_obj->size = sec_dat.size & 0xFFFFFFFF;
FREE_IF (_obj->unknown_bits);
_obj->unknown_bits = bit_read_TF (&sec_dat, _obj->size);
bit_set_position (&sec_dat, 0);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error = appinfo_private (dat, dwg);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
// may return OUTOFBOUNDS
static int
filedeplist_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Dwg_FileDepList *_obj = &dwg->filedeplist;
Dwg_Object *obj = NULL; // for obj->size overflow check
int error = 0;
BITCODE_BL vcount;
// clang-format off
#include "filedeplist.spec"
// clang-format on
return error;
}
/* r18 FileDepList Section
*/
static int
read_2004_section_filedeplist (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
// not compressed, page size: 0x80. 0xc or 0xd
error
= read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_FILEDEPLIST);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", "FileDepList");
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_fd.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("FileDepList (%" PRIuSIZE ")\n-------------------\n",
sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error = filedeplist_private (dat, dwg);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
// may return OUTOFBOUNDS
static int
security_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_Security *_obj = &dwg->security;
Dwg_Object *obj = NULL;
int error = 0;
memset (_obj, 0, sizeof (Dwg_Security));
// clang-format off
#include "security.spec"
// clang-format on
return error;
}
/* r18 Security Section, if saved with password
*/
static int
read_2004_section_security (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
// compressed, page size: 0x7400
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_SECURITY);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", "Security");
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_sec.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("Security (%" PRIuSIZE ")\n-------------------\n", sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error = security_private (dat, dwg);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
#if 0
static int
signature_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
struct Dwg_Signature *_obj = &dwg->signature;
Dwg_Object *obj = NULL;
int error = 0;
// clang-format off
#include "signature.spec"
// clang-format on
return error;
}
/* Signature Section, not written nor documented by Teigha
*/
static int
read_2004_section_signature (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
const char *secname = "Signature";
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_SIGNATURE);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", secname);
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
# ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE) {
FILE *fp = fopen ("decomp_sig.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose(fp);
}
# endif
LOG_TRACE ("Signature (%" PRIuSIZE ")\n-------------------\n", sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error |= signature_private (dat, dwg);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
#endif
/* VBAProject Section. TODO dwg_decode_ole2
*/
static int
read_2004_section_vbaproject (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
Dwg_VBAProject *_obj = &dwg->vbaproject;
if (!dwg->header.vbaproj_address)
{
LOG_INFO ("No %s section. Empty header.vbaproj_address\n", "VBAProject");
return 0;
}
// compressed
error
= read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_VBAPROJECT);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", "VBAProject");
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_vba.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("VBAProject (%" PRIuSIZE ")\n-------------------\n",
sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
// DEBUG_HERE dwg_decode_ole2 () (MS-CFB starts at offset 0x10)
// https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-ovba/575462ba-bf67-4190-9fac-c275523c75fc
_obj->size = dat->size & 0xFFFFFFFF;
_obj->unknown_bits = bit_read_TF (dat, _obj->size);
LOG_TRACE_TF (_obj->unknown_bits, _obj->size);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
/* AppInfoHistory Section
*/
static int
read_2004_section_appinfohistory (Bit_Chain *restrict dat,
Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
Dwg_AppInfoHistory *_obj = &dwg->appinfohistory;
// compressed
error = read_2004_compressed_section (dat, dwg, &sec_dat,
SECTION_APPINFOHISTORY);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", "AppInfoHistory");
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_aih.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("AppInfoHistory (%" PRIuSIZE ")\n-------------------\n",
sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
// DEBUG_HERE
_obj->size = dat->size & 0xFFFFFFFF;
_obj->unknown_bits = bit_read_TF (dat, _obj->size);
LOG_TRACE_TF (_obj->unknown_bits, _obj->size);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
// may return OUTOFBOUNDS
static int
revhistory_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_RevHistory *_obj = &dwg->revhistory;
Dwg_Object *obj = NULL;
int error = 0;
// clang-format off
#include "revhistory.spec"
// clang-format on
return error;
}
/* RevHistory Section
*/
static int
read_2004_section_revhistory (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
// compressed
error
= read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_REVHISTORY);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", "RevHistory");
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_rh.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("RevHistory (%" PRIuSIZE ")\n-------------------\n",
sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error = revhistory_private (dat, dwg);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
/* ObjFreeSpace Section
*/
static int
read_2004_section_objfreespace (Bit_Chain *restrict dat,
Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
Dwg_ObjFreeSpace *_obj = &dwg->objfreespace;
// compressed
error = read_2004_compressed_section (dat, dwg, &sec_dat,
SECTION_OBJFREESPACE);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", "ObjFreeSpace");
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_ofs.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("ObjFreeSpace (%" PRIuSIZE ")\n-------------------\n",
sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
// clang-format off
#include "objfreespace.spec"
// clang-format on
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
/* Template Section. Optional r2004, mandatory r2007+ (but violated by Teigha)
Contains the MEASUREMENT variable (0 = English, 1 = Metric).
*/
static int
read_2004_section_template (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
// compressed
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_TEMPLATE);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
UNTIL (R_2004)
{
LOG_INFO ("%s section not found\n", "Template");
}
LATER_VERSIONS
{
// violated by Teigha 4.3.2
LOG_ERROR ("%s section not found\n", "Template");
}
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_temp.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("Template (%" PRIuSIZE ")\n-------------------\n", sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error |= template_private (dat, dwg);
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
static int
acds_private (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain *str_dat = dat;
Dwg_AcDs *_obj = &dwg->acds;
Dwg_Object *obj = NULL;
BITCODE_BL rcount3 = 0, rcount4, vcount;
int error = 0;
// clang-format off
#include "acds.spec"
// clang-format on
return error;
}
/* datastorage section. Optional, since r2013
Contains the SAB binary ACIS data, version 2
*/
static int
read_2004_section_acds (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain old_dat, sec_dat = { 0 };
int error;
const char *secname = "AcDsPrototype_1b";
// compressed, pagesize 0x7400, type 13
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_ACDS);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_INFO ("%s section not found\n", secname);
if (sec_dat.chain)
free (sec_dat.chain);
return 0;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_acds.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
LOG_TRACE ("AcDs datastorage (%" PRIuSIZE ")\n-------------------\n",
sec_dat.size);
old_dat = *dat;
dat = &sec_dat; // restrict in size
bit_chain_set_version (&old_dat, dat);
error |= acds_private (dat, dwg);
// also with a damaged segment, after the data index
acds_assign_sab (dwg, dat, &dwg->acds);
error &= ~DWG_ERR_SECTIONNOTFOUND;
LOG_TRACE ("\n");
if (sec_dat.chain)
free (sec_dat.chain);
*dat = old_dat; // unrestrict
return error;
}
static int // thumbnail
read_2004_section_preview (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain sec_dat = { 0 };
int error = 0;
BITCODE_RL size;
BITCODE_RC type;
unsigned char *sentinel;
// not compressed, num_sections: 1
error = read_2004_compressed_section (dat, dwg, &sec_dat, SECTION_PREVIEW);
if (error >= DWG_ERR_CRITICAL || !sec_dat.chain)
{
LOG_ERROR ("Failed to read uncompressed %s section", "Preview");
if (sec_dat.chain)
free (sec_dat.chain);
return error;
}
#ifdef DEBUG
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
FILE *fp = fopen ("decomp_pre.bin", "wb");
fwrite (sec_dat.chain, 1, sec_dat.size, fp);
fclose (fp);
}
#endif
if (dwg->header.thumbnail_address != (BITCODE_RL)dat->byte)
LOG_WARN ("thumbnail_address mismatch: " FORMAT_RL " != %" PRIuSIZE,
dwg->header.thumbnail_address, dat->byte);
LOG_TRACE ("Preview (%" PRIuSIZE ")\n-------------------\n", sec_dat.size);
if (!sec_dat.chain || sec_dat.size < 32)
{
LOG_WARN ("Empty thumbnail");
if (sec_dat.chain)
free (sec_dat.chain);
return error;
}
sentinel = (unsigned char *)dwg_sentinel (DWG_SENTINEL_THUMBNAIL_BEGIN);
if (memcmp (sentinel, sec_dat.chain, 16))
{
LOG_WARN ("thumbnail sentinel mismatch");
free (sec_dat.chain);
return error;
}
assert (sec_dat.size >= 32);
assert (sec_dat.chain);
dwg->thumbnail.size = sec_dat.size - 16;
dwg->thumbnail.chain = sec_dat.chain;
dwg->thumbnail.byte = 16; // sentinel
dwg_bmp (dwg, &size, &type);
// various headers
if (abs ((int)((long)size - (long)dwg->thumbnail.size)) > 200)
LOG_WARN ("thumbnail.size mismatch: %" PRIuSIZE " != " FORMAT_RL,
dwg->thumbnail.size, size);
dat->byte += dwg->thumbnail.size;
return error;
}
/* For decrypt and encrypt: symmetric, as it's just a simple XOR with a
one-time pad, generated here on the fly. */
void
decrypt_R2004_header (BITCODE_RC *restrict dest,
const BITCODE_RC *restrict src, size_t size)
{
uint32_t rseed = 1;
for (size_t i = 0; i < size; i++)
{
rseed *= 0x343fd;
rseed += 0x269ec3;
dest[i] = src[i] ^ (rseed >> 0x10);
}
}
GCC30_DIAG_IGNORE (-Wformat-nonliteral)
static int
decode_R2004_header (Bit_Chain *restrict file_dat, Dwg_Data *restrict dwg)
{
int error = 0;
Dwg_Object *obj = NULL;
Dwg_R2004_Header *_obj = &dwg->fhdr.r2004_header;
Bit_Chain *hdl_dat = file_dat;
{
const size_t size = sizeof (Dwg_R2004_Header);
BITCODE_RC decrypted_data[sizeof (Dwg_R2004_Header)];
Bit_Chain decrypted_header_dat = *file_dat;
Bit_Chain *dat;
BITCODE_RL crc32, calc_crc32;
decrypted_header_dat.size = size;
decrypted_header_dat.chain = decrypted_data;
decrypted_header_dat.byte = decrypted_header_dat.bit = 0;
if (file_dat->size < size + (file_dat->byte + 80))
{
LOG_ERROR ("Size underflow %" PRIuSIZE " for R2004_Header",
file_dat->size);
return DWG_ERR_INVALIDDWG;
}
LOG_HANDLE ("encrypted R2004_Header (@%u.0-%" PRIuSIZE ".0, %" PRIuSIZE
"):\n",
0x80, size + 0x80, size);
LOG_TF_HEX (HANDLE, &file_dat->chain[0x80], (int)size);
decrypt_R2004_header (decrypted_data, &file_dat->chain[0x80], size);
LOG_HANDLE ("decrypted R2004_Header (@%u.0-%" PRIuSIZE ".0, %" PRIuSIZE
"):\n",
0x80, size + 0x80, size);
LOG_TF_HEX (HANDLE, &decrypted_data[0], size);
dat = &decrypted_header_dat;
dat->bit = 0;
dat->byte = 0UL;
LOG_TRACE ("\n#### r2004 File Header ####\n");
// clang-format off
#include "r2004_file_header.spec"
// clang-format on
if (memcmp (_obj->file_ID_string, "AcFssFcAJMB", 8))
{
LOG_ERROR ("r2004_file_header decryption error");
return error | DWG_ERR_INVALIDDWG;
}
// only really needed for r2004 encode later
crc32 = _obj->crc32;
_obj->crc32 = 0;
// while calc crc the 4 CRC bytes should initialized to 0 zero
decrypted_data[0x6c - 1] = 0;
decrypted_data[0x6c - 2] = 0;
decrypted_data[0x6c - 3] = 0;
decrypted_data[0x6c - 4] = 0;
// without the padding, but the crc32 as 0
calc_crc32 = bit_calc_CRC32 (0, &decrypted_data[0], 0x6c);
_obj->crc32 = crc32;
if (calc_crc32 != crc32)
{
LOG_INFO ("r2004_file_header CRC32 mismatch 0x%08x != 0x%08x\n",
calc_crc32, crc32);
}
else
{
LOG_TRACE ("crc32 => 0x%08x (verified)\n", (unsigned)crc32);
}
}
/*-------------------------------------------------------------------------
* Section Page Map
*/
{
BITCODE_RL checksum1, checksum2;
Bit_Chain *dat = file_dat;
size_t start;
BITCODE_RC *map;
LOG_INSANE ("@0x%zx\n", dat->byte);
LOG_TRACE ("\n=== Read System Section (Section Page Map) @%lx ===\n\n",
(unsigned long)dwg->fhdr.r2004_header.section_map_address
+ 0x100);
dat->byte = dwg->fhdr.r2004_header.section_map_address + 0x100;
start = dwg->fhdr.r2004_header.section_map_address;
// Some section_map_address overflow past the dwg. GH #617
// maybe search the magic type backwards then. (in 0x20 page boundary
// steps) e.g. 1344464555_1_2004
if (start > dat->size)
{
BITCODE_RL section_type;
start = dat->size & 0xffffff00;
LOG_INFO ("Searching for the Section Page Map backwards @%zx ...\n",
start - 0x20);
do
{
start -= 0x20;
dat->byte = start;
section_type = bit_read_RL (dat);
LOG_INSANE ("section_type: " FORMAT_RLx " @%zx\n", section_type,
dat->byte - 4);
}
while (section_type != 0x41630e3b && dat->byte > 0x120);
if (section_type != 0x41630e3b)
LOG_INFO ("Not found\n");
}
/* start may point near the end of the file (small or truncated
files), where reading the 0x100-byte page map header would run
past the end of the chain buffer. GHSA-c8cq-gr5h-7c6j */
if (start > dat->size || dat->size - start < 0x100)
{
LOG_ERROR ("Invalid Section Page Map address 0x%zx (file size "
"0x%zx)\n",
start, dat->size);
return DWG_ERR_VALUEOUTOFBOUNDS;
}
map = &dat->chain[start];
LOG_INSANE ("section_map_address: 0x%zx + 0x100:\n", start);
LOG_INSANE_TF (map, 0x100);
LOG_INSANE ("@0x%zx\n", dat->byte);
FIELD_RLx (section_type, 0);
if (FIELD_VALUE (section_type) != 0x41630e3b)
{
dat->byte = start + 0x100 + 16;
LOG_ERROR ("Invalid System Section Page Map type 0x%x != 0x41630e3b",
FIELD_VALUE (section_type));
return DWG_ERR_SECTIONNOTFOUND;
}
FIELD_RL (decomp_data_size, 0);
FIELD_RL (comp_data_size, 0);
FIELD_RL (compression_type, 0);
FIELD_RLx (checksum, 0);
LOG_INSANE ("@0x%zx\n", dat->byte);
// get the seed from the header (skipping the checksum)
dat->byte = start + 0x100;
checksum1 = dwg_section_page_checksum (0, dat, 20, true);
// now the compressed buffer, not the header. not skipping the checksum
dat->byte = start + 0x114;
checksum2 = dwg_section_page_checksum (checksum1, dat,
_obj->comp_data_size, false);
LOG_TRACE ("checksum => 0x%08x with calculated seed 0x%08x\n",
(unsigned)checksum2, (unsigned)checksum1);
if (checksum2 == _obj->checksum)
{
LOG_TRACE ("checksum: 0x%08x (verified)\n", (unsigned)checksum2);
}
else
{
LOG_WARN ("checksum: 0x%08x (calculated) page checksum mismatch "
"0x%zx-0x%zx\n",
(unsigned)checksum2, start + 0x100, start + 0x114);
error |= DWG_ERR_WRONGCRC;
}
}
return error;
}
/* for 2004 and 2010+ */
static int
decode_R2004 (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
int error = 0;
Dwg_Section *section;
{
Dwg_Header *_obj = &dwg->header;
Dwg_Object *obj = NULL;
Bit_Chain *hdl_dat = dat;
int i;
BITCODE_BL vcount;
assert (dat->byte == 0xb); // after version magic
// clang-format off
#include "header.spec"
// clang-format on
}
if ((dat->byte - 54) > 0x80)
{
LOG_HANDLE ("\nempty R2004 slack (@%" PRIuSIZE ".0-%u.0, %ld):\n",
dat->byte - 54, 0x80, (long)(0x80 - (dat->byte - 54)));
if (dat->byte > 54 && dat->chain)
LOG_TF (HANDLE, &dat->chain[dat->byte],
(int)(0x80 - (dat->byte - 54)));
}
error |= decode_R2004_header (dat, dwg);
if (error > DWG_ERR_CRITICAL)
return error;
error |= read_R2004_section_map (dat, dwg);
if (!dwg->header.section || error >= DWG_ERR_CRITICAL)
{
LOG_ERROR ("Failed to read R2004 Section Page Map.");
return error | DWG_ERR_INTERNALERROR;
}
/*-------------------------------------------------------------------------
* Section Info
*/
section = find_section (dwg, dwg->fhdr.r2004_header.section_info_id);
if (section)
{
Dwg_Object *obj = NULL;
Dwg_Section *_obj = section;
LOG_TRACE ("\n=== Data Section (Section Info %d) @%lx ===\n",
dwg->fhdr.r2004_header.section_info_id,
(unsigned long)section->address);
dat->byte = section->address;
FIELD_RLx (section_type, 0);
if (FIELD_VALUE (section_type) != 0x4163003b)
{
LOG_ERROR ("Invalid Data Section Page Map type 0x%x != 0x4163003b",
FIELD_VALUE (section_type));
return DWG_ERR_SECTIONNOTFOUND;
}
FIELD_RL (decomp_data_size, 0);
FIELD_RL (comp_data_size, 0);
FIELD_RL (compression_type, 0);
FIELD_RLx (checksum, 0);
// Data section map, par 4.5
error |= read_R2004_section_info (dat, dwg, _obj->comp_data_size,
_obj->decomp_data_size);
}
else
error |= DWG_ERR_SECTIONNOTFOUND;
error |= read_2004_section_header (dat, dwg);
if (dwg->header.summaryinfo_address)
error |= read_2004_section_summary (dat, dwg);
error |= read_2004_section_classes (dat, dwg);
error |= read_2004_section_handles (dat, dwg);
error |= read_2004_section_auxheader (dat, dwg);
if (dwg->header.thumbnail_address)
error |= read_2004_section_preview (dat, dwg);
error |= read_2004_section_appinfo (dat, dwg);
error |= read_2004_section_appinfohistory (dat, dwg);
error |= read_2004_section_filedeplist (dat, dwg);
error |= read_2004_section_security (dat, dwg);
error |= read_2004_section_revhistory (dat, dwg);
error |= read_2004_section_objfreespace (dat, dwg);
error |= read_2004_section_template (dat, dwg);
if (dwg->header.vbaproj_address)
error |= read_2004_section_vbaproject (dat, dwg);
// error |= read_2004_section_signature (dat, dwg);
error |= read_2004_section_acds (dat, dwg);
/* Clean up. XXX? Need this to write the sections, at least the name and
* type
*/
#if 0
if (dwg->header.section_info != NULL)
{
unsigned u;
for (u = 0; u < dwg->header.num_infos; ++u)
if (dwg->header.section_info[u].sections != 0)
free(dwg->header.section_info[u].sections);
free(dwg->header.section_info);
dwg->header.num_infos = 0;
}
#endif
LOG_INFO ("\nnum_objects: %lu\n", (unsigned long)dwg->num_objects);
LOG_TRACE ("num_object_refs: %lu\n", (unsigned long)dwg->num_object_refs);
LOG_TRACE ("Resolving pointers from ObjectRef vector:\n");
error |= resolve_objectref_vector (dat, dwg);
return error;
}
static int
decode_R2007 (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
Bit_Chain hdl_dat = { 0 };
int error;
hdl_dat = *dat;
{
Dwg_Header *_obj = &dwg->header;
Dwg_Object *obj = NULL;
int i;
BITCODE_BL vcount;
assert (dat->byte == 0xb); // after version magic
// clang-format off
#include "header.spec"
// clang-format on
}
// this includes classes, header, handles + objects
error = read_r2007_meta_data (dat, &hdl_dat, dwg);
LOG_INFO ("\nnum_objects: %lu\n", (unsigned long)dwg->num_objects);
LOG_TRACE ("num_object_refs: %lu\n", (unsigned long)dwg->num_object_refs);
if (error >= DWG_ERR_CRITICAL)
{
LOG_ERROR ("Failed to read 2007 meta data");
return error;
}
LOG_TRACE ("Resolving pointers from ObjectRef vector:\n");
return error | resolve_objectref_vector (dat, dwg);
}
/*--------------------------------------------------------------------------------
* Private functions
*
* EED "Extended Entity Data":
* There's an array of obj->num_eed obj->eed[] entries.
* Each eed member has size, handle, the raw[size] buffer and the decoded data.
* Each obj->eed[].data member is further separated into DXF+1000 codes, for
* strings, numbers, points, ...
* Those subgroups have an empty raw, size, and the prev. handle.
*/
#undef LOG_POS
#define LOG_POS LOG_RPOS
static int
eed_need_size (unsigned long need, BITCODE_BS have)
{
// 1 more for the initial eed.data.code byte
if ((need + 1) > (unsigned long)have)
{
LOG_ERROR ("Invalid EED size %lu > %u", need, (unsigned)have);
return 1;
}
return 0;
}
static int
dwg_decode_eed_data (Bit_Chain *restrict dat, Dwg_Eed_Data *restrict data,
size_t end, BITCODE_BS size)
{
int lenc;
BITCODE_BS j;
size_t lens;
data->code = bit_read_RC (dat);
LOG_TRACE ("code: %d [RC], ", (int)data->code);
switch (data->code)
{
case 0:
PRE (R_13b1)
{
if (eed_need_size (1, size))
return DWG_ERR_INVALIDEED;
data->u.eed_0.is_tu = 0;
data->u.eed_0.length = lenc = bit_read_RC (dat);
if ((long)lenc > size - 3)
{
LOG_ERROR ("Invalid EED string len %d, max %d", lenc,
(int)size - 3);
dat->byte = end;
break;
}
/* code:1 + len:1 */
if (eed_need_size (lenc + 2, size))
return DWG_ERR_INVALIDEED;
bit_read_fixed (dat, (BITCODE_RC *)data->u.eed_0.string, lenc);
data->u.eed_0.string[lenc] = '\0';
LOG_TRACE ("string: len=%d [RC] \"%s\" [TF]", (int)lenc,
data->u.eed_0.string);
}
VERSIONS (R_13b1, R_2007b)
{
if (eed_need_size (3, size))
return DWG_ERR_INVALIDEED;
data->u.eed_0.is_tu = 0;
data->u.eed_0.length = lenc = bit_read_RC (dat);
data->u.eed_0.codepage = bit_read_RS_BE (dat);
if ((long)lenc > size - 4)
{
LOG_ERROR ("Invalid EED string len %d, max %d", lenc,
(int)size - 4);
dat->byte = end;
break;
#if 0
obj->num_eed = 0;
if (obj->eed[idx].size)
free(obj->eed[idx].raw);
free(obj->eed[idx].data);
free(obj->eed);
obj->eed = NULL;
dat->byte = end;
return DWG_ERR_INVALIDEED; /* may not continue */
#endif
}
/* code:1 + len:1 + cp:2 */
if (eed_need_size (lenc + 3, size))
return DWG_ERR_INVALIDEED;
bit_read_fixed (dat, (BITCODE_RC *)data->u.eed_0.string, lenc);
data->u.eed_0.string[lenc] = '\0';
LOG_TRACE ("string: len=%d [RC] cp=%d [RS_BE] \"%s\" [TF]", (int)lenc,
(int)data->u.eed_0.codepage, data->u.eed_0.string);
}
SINCE (R_2007a)
{
if (eed_need_size (2, size))
return DWG_ERR_INVALIDEED;
data->u.eed_0.is_tu = 1;
data->u.eed_0_r2007.length = bit_read_RS (dat);
lens = data->u.eed_0_r2007.length;
if (eed_need_size ((long)(lens * 2) + 2, size))
return DWG_ERR_INVALIDEED;
/* code:1 + len:2 NUL? */
for (j = 0;
j < (BITCODE_BS)MIN (lens, (unsigned long)((size - 3) / 2)); j++)
data->u.eed_0_r2007.string[j] = bit_read_RS (dat);
// data->u.eed_0_r2007.string[j] = 0; //already calloc'ed
#ifdef _WIN32
LOG_TRACE ("wstring: len=%d [RS] \"" FORMAT_TU "\" [TU]", (int)lens,
data->u.eed_0_r2007.string);
#else
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_TRACE)
{
char *u8 = bit_TU_to_utf8_len (data->u.eed_0_r2007.string, lens);
LOG_TRACE ("wstring: len=%d [RS] \"%s\" [TU]", (int)lens, u8);
free (u8);
}
#endif
}
break;
case 1:
if (eed_need_size (3, size))
return DWG_ERR_INVALIDEED;
data->u.eed_1.appid_index = bit_read_RS (dat);
LOG_TRACE ("appid_index: " FORMAT_RS " [RS]", data->u.eed_1.appid_index);
break;
case 2:
if (eed_need_size (1, size))
return DWG_ERR_INVALIDEED;
data->u.eed_2.close = bit_read_RC (dat);
if (data->u.eed_2.close)
{
LOG_TRACE ("close: " FORMAT_RC " [RC]", data->u.eed_2.close);
}
else
{
LOG_TRACE ("open: " FORMAT_RC " [RC]", data->u.eed_2.close);
}
break;
case 3:
if (eed_need_size (8, size))
return DWG_ERR_INVALIDEED;
PRE (R_13b1)
{
data->u.eed_3.layer = (BITCODE_RLL)bit_read_RS (dat);
LOG_TRACE ("layer: " FORMAT_RLL " [RS]", data->u.eed_3.layer);
}
LATER_VERSIONS
{
data->u.eed_3.layer = bit_read_RLL_BE (dat);
LOG_TRACE ("layer: " FORMAT_HV " [RLL_BE]", data->u.eed_3.layer);
}
break;
case 4:
if (eed_need_size (1, size))
return DWG_ERR_INVALIDEED;
data->u.eed_4.length = lenc = bit_read_RC (dat);
if (eed_need_size (lenc + 1, size))
return DWG_ERR_INVALIDEED;
/* code:1 + len:1 */
for (j = 0; j < MIN (lenc, size - 2); j++)
data->u.eed_4.data[j] = bit_read_RC (dat);
LOG_TRACE ("binary[%d] \"%s\" [TF]", j - 1, data->u.eed_4.data);
break;
case 5:
if (eed_need_size (8, size))
return DWG_ERR_INVALIDEED;
data->u.eed_5.entity = bit_read_RLL_BE (dat);
LOG_TRACE ("entity: " FORMAT_HV " [RLL_BE]", data->u.eed_5.entity);
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
if (eed_need_size (3 * 8, size))
return DWG_ERR_INVALIDEED;
data->u.eed_10.point.x = bit_read_RD (dat);
data->u.eed_10.point.y = bit_read_RD (dat);
data->u.eed_10.point.z = bit_read_RD (dat);
LOG_TRACE ("3dpoint: (%f, %f, %f) [3RD]", data->u.eed_10.point.x,
data->u.eed_10.point.y, data->u.eed_10.point.z);
break;
case 40:
case 41:
case 42:
if (eed_need_size (8, size))
return DWG_ERR_INVALIDEED;
data->u.eed_40.real = bit_read_RD (dat);
LOG_TRACE ("real: %f [RD]", data->u.eed_40.real);
break;
case 70:
if (eed_need_size (2, size))
return DWG_ERR_INVALIDEED;
data->u.eed_70.rs = bit_read_RS (dat);
LOG_TRACE ("short: " FORMAT_RS " [RS]", data->u.eed_70.rs);
break;
case 71:
if (eed_need_size (4, size))
return DWG_ERR_INVALIDEED;
data->u.eed_71.rl = bit_read_RL (dat);
LOG_TRACE ("long: " FORMAT_RL " [RL]", data->u.eed_71.rl);
if (data->u.eed_71.rl > 0x100000)
LOG_TRACE (" " FORMAT_RLx " [RLx]", data->u.eed_71.rl);
break;
default:
LOG_ERROR ("unknown EED code %d", data->code);
LOG_POS
dat->byte = end;
LOG_POS
return DWG_ERR_INVALIDEED; /* may continue */
}
LOG_POS
return 0;
}
/* for objects and entities */
int
dwg_decode_eed (Bit_Chain *restrict dat, Dwg_Object_Object *restrict obj)
{
BITCODE_BS size = 0;
int error = 0;
unsigned int idx = 0;
Dwg_Data *dwg = obj->dwg;
Dwg_Object *_obj;
size_t sav_byte = dat->byte;
if (!dwg)
return DWG_ERR_INVALIDEED;
_obj = &dwg->object[obj->objid]; /* Note that obj->objid may be 0 */
obj->num_eed = 0;
while (1)
{
BITCODE_BS j;
size_t end;
if (dat->from_version >= R_13b1)
{
size = bit_read_BS (dat);
if (!size)
break;
LOG_TRACE ("EED[%u] size: " FORMAT_BS " [BS]", idx, size);
}
else
{
if (idx)
break;
size = (BITCODE_BS)bit_read_RS (dat);
LOG_TRACE ("EED[%u] size: " FORMAT_BS " [RS]", idx, size);
}
LOG_RPOS
if (size > _obj->size
|| (dat->from_version < R_13b1 && dat->byte == sav_byte))
{
LOG_ERROR ("Invalid EED size " FORMAT_BS " > %u", size, _obj->size);
obj->num_eed = idx;
return DWG_ERR_INVALIDEED;
}
obj->num_eed = idx + 1;
if (idx) /* just extend by one. not in chunks for now */
{
obj->eed
= (Dwg_Eed *)realloc (obj->eed, obj->num_eed * sizeof (Dwg_Eed));
memset (&obj->eed[idx], 0, sizeof (Dwg_Eed));
}
else
{
obj->eed = (Dwg_Eed *)calloc (1, sizeof (Dwg_Eed));
}
obj->eed[idx].size = size;
if (dat->from_version >= R_13b1)
{
error |= bit_read_H (dat, &obj->eed[idx].handle);
end = dat->byte + size;
if (error)
{
LOG_ERROR ("No EED[%d].handle", idx);
obj->eed[idx].size = 0;
obj->num_eed--;
if (!obj->num_eed)
dwg_free_eed (_obj);
dat->byte = end; // skip eed
continue; // continue for size = bit_read_BS(dat)
}
else
{
LOG_TRACE ("EED[%u] handle: " FORMAT_H, idx,
ARGS_H (obj->eed[idx].handle));
LOG_RPOS;
if (dat->byte >= dat->size)
end = dat->byte;
if (_obj->fixedtype == DWG_TYPE_MLEADERSTYLE)
{ // check for is_new_format: has extended data for APPID
// “ACAD_MLEADERVER”
Dwg_Object_Ref ref;
ref.obj = NULL;
ref.handleref = obj->eed[idx].handle;
ref.absolute_ref = 0L;
if (dwg_resolve_handleref (&ref, _obj))
{
Dwg_Object *appid
= dwg_get_first_object (dwg, DWG_TYPE_APPID_CONTROL);
if (appid)
{
Dwg_Object_APPID_CONTROL *_appid
= appid->tio.object->tio.APPID_CONTROL;
// search absref in APPID_CONTROL apps[]
for (j = 0; j < _appid->num_entries; j++)
{
if (_appid->entries && _appid->entries[j]
&& _appid->entries[j]->absolute_ref
== ref.absolute_ref)
{
Dwg_Object_MLEADERSTYLE *mstyle
= obj->tio.MLEADERSTYLE;
// real value with code 70 follows
mstyle->class_version = 2;
LOG_TRACE (
"EED found ACAD_MLEADERVER " FORMAT_HV
"\n",
ref.absolute_ref);
}
}
}
}
}
}
}
else
{
end = dat->byte + size;
}
sav_byte = dat->byte;
obj->eed[idx].raw = bit_read_TF (dat, size);
LOG_TRACE ("EED[%u] raw: %d\n", idx, size);
LOG_INSANE_TF (obj->eed[idx].raw, size);
// LOG_TRACE ("\n");
dat->byte = sav_byte;
// LOG_RPOS
while (dat->byte < end)
{
obj->eed[idx].data = (Dwg_Eed_Data *)calloc (size + 8, 1);
LOG_TRACE ("EED[%u] ", idx);
error |= dwg_decode_eed_data (dat, obj->eed[idx].data, end, size);
// overflow or no advance
if (dat->byte >= dat->size || dat->byte == sav_byte)
error |= DWG_ERR_INVALIDEED;
if (error & DWG_ERR_INVALIDEED)
{
free (obj->eed[idx].data);
LOG_HANDLE (" invalid eed[%d]: skip", idx);
LOG_RPOS
obj->eed[idx].data = NULL;
// obj->num_eed--; // we still have .raw
dat->byte = end; // skip eed
continue; // continue for next size = bit_read_BS(dat)
}
// Shrink to the bytes this item actually consumed, not the
// remaining EED block: an object with many small EED items
// would otherwise take memory quadratic in the number of
// items. The +8 covers the string header/terminator the
// decoder stores beyond the read bytes; sizeof covers the
// fixed union members.
{
Dwg_Eed_Data *tmp;
size_t used = (size_t)(dat->byte - sav_byte) + 8;
if (used < sizeof (Dwg_Eed_Data))
used = sizeof (Dwg_Eed_Data);
tmp = (Dwg_Eed_Data *)realloc (obj->eed[idx].data, used);
if (tmp)
obj->eed[idx].data = tmp;
}
if (_obj->fixedtype == DWG_TYPE_MLEADERSTYLE
&& obj->tio.MLEADERSTYLE->class_version == 2
&& obj->eed[idx].data->code == 70)
{
obj->tio.MLEADERSTYLE->class_version
= obj->eed[idx].data->u.eed_70.rs;
LOG_TRACE ("ACAD_MLEADERVER class_version: %d\n",
obj->eed[idx].data->u.eed_70.rs);
}
if (dat->byte < end - 1)
{
idx++;
obj->num_eed = idx + 1;
size = (long)(end - dat->byte + 1);
// LOG_INSANE (" size remaining: %ld\n", (long)size);
// LOG_RPOS
obj->eed = (Dwg_Eed *)realloc (obj->eed,
obj->num_eed * sizeof (Dwg_Eed));
obj->eed[idx].handle = obj->eed[idx - 1].handle;
obj->eed[idx].size = 0;
obj->eed[idx].raw = NULL;
sav_byte = dat->byte;
}
else
{
break;
}
}
LOG_RPOS
if (obj->eed[idx].raw)
{
dat->byte = end;
LOG_RPOS
}
idx++;
}
LOG_HANDLE ("EED[%u] size: " FORMAT_BS " (end)", idx, size);
LOG_RPOS
LOG_TRACE ("num_eed: " FORMAT_BL "\n", obj->num_eed);
return error;
}
/** OL2FRAME.data potentially contains as 128 byte of custom specific data:
BITCODE_BS oleversion; DXF 70, always 2
BOTCODE_TF oleclient; DXF 3, e.g. OLE or Paintbrush Picture
BITCODE_3RD pt1; DXF 10, upper left corner
BITCODE_3RD pt2; DXF 11, lower right corner
plus eventually:
T link_name; like C:\My Documents\excel.xls!Sheet1!R5C3:R8C3
RC output_quality;
BD rotation
BD width;
BD height;
BD scale_width;
BD scale_height;
The rest of data (&data[128]) contains the MS-CFB, see
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b
e.g. decodable via py-oletools
*/
static int
dwg_decode_ole2 (Dwg_Entity_OLE2FRAME *restrict _obj)
{
Bit_Chain bdat;
Bit_Chain *dat = &bdat;
dat->bit = 0;
dat->byte = 0;
dat->size = 0x80;
dat->chain = (unsigned char *)&_obj->data[0];
dat->version = _obj->parent->dwg->header.version;
dat->from_version = _obj->parent->dwg->header.from_version;
// TODO decode the unknowns
/* Sample data from TS1.dwg:
00000000: 8055 40f9 3284 d222 3e40 7436 e0d9 23fd .U@.2..">@t6..#.
00000010: 32c0 0000 0000 0000 0000 d879 8900 cda2 2..........y....
00000020: 4140 7436 e0d9 23fd 32c0 0000 0000 0000 A@t6..#.2.......
00000030: 0000 d879 8900 cda2 4140 1420 d4f3 b864 ...y....A@. ...d
00000040: 36c0 0000 0000 0000 0000 40f9 3284 d222 6.........@.2.."
00000050: 3e40 1420 d4f3 b864 36c0 0000 0000 0000 >@. ...d6.......
00000060: 0000 021f 9114 0100 0000 0001 0000 0100 ................
00000070: 0000 0100 0000 0000 0100 0000 0090 0500 ................
=> from the DXF
oleversion 2 [70]
oleclient "OLE" [3] (but the cfb contains PBrush.9)
pt1 (30.13602472538446, -18.98882829402869, 0.0) [10]
pt2 (35.27188116753285, -22.39344715050545, 0.0) [11]
*/
// FIXME decode the fields
// FIELD_BS (oleversion, 70);
// FIELD_TV (oleclient, 3);
// FIELD_2BD (pt1, 10);
// FIELD_2BD (pt2, 11);
_obj->oleversion = 2;
_obj->oleclient = (BITCODE_TF) "OLE";
_obj->pt1.x = 30.13602472538446;
_obj->pt1.y = -18.98882829402869;
_obj->pt2.x = 35.27188116753285;
_obj->pt2.y = -22.39344715050545;
// next, see the MS-CFB format
dat->bit = 0;
dat->byte = 0;
dat->size = _obj->data_size;
dat->chain = (unsigned char *)&_obj->data[0x80];
// TODO
return 0;
}
// for all obj->type < 500. if to check for the has_strings bit after bitsize
// TODO: generate this automatically
static int
obj_has_strings (unsigned int type)
{
switch (type)
{
case DWG_TYPE_TEXT:
case DWG_TYPE_ATTRIB:
case DWG_TYPE_ATTDEF:
case DWG_TYPE_BLOCK:
return 1;
case DWG_TYPE_ENDBLK:
case DWG_TYPE_SEQEND:
case DWG_TYPE_INSERT:
case DWG_TYPE_MINSERT:
case DWG_TYPE_VERTEX_2D:
case DWG_TYPE_VERTEX_3D:
case DWG_TYPE_VERTEX_MESH:
case DWG_TYPE_VERTEX_PFACE:
case DWG_TYPE_VERTEX_PFACE_FACE:
case DWG_TYPE_POLYLINE_2D:
case DWG_TYPE_POLYLINE_3D:
case DWG_TYPE_ARC:
case DWG_TYPE_CIRCLE:
case DWG_TYPE_LINE:
return 0;
case DWG_TYPE_DIMENSION_ORDINATE:
case DWG_TYPE_DIMENSION_LINEAR:
case DWG_TYPE_DIMENSION_ALIGNED:
case DWG_TYPE_DIMENSION_ANG3PT:
case DWG_TYPE_DIMENSION_ANG2LN:
case DWG_TYPE_DIMENSION_RADIUS:
case DWG_TYPE_DIMENSION_DIAMETER:
return 1;
case DWG_TYPE_POINT:
case DWG_TYPE__3DFACE:
case DWG_TYPE_POLYLINE_PFACE:
case DWG_TYPE_POLYLINE_MESH:
case DWG_TYPE_SOLID:
case DWG_TYPE_TRACE:
case DWG_TYPE_SHAPE:
return 0;
case DWG_TYPE_VIEWPORT:
return 1;
case DWG_TYPE_ELLIPSE:
case DWG_TYPE_SPLINE:
return 0;
case DWG_TYPE_REGION:
case DWG_TYPE__3DSOLID:
case DWG_TYPE_BODY:
return 1;
case DWG_TYPE_RAY:
case DWG_TYPE_XLINE:
return 0;
case DWG_TYPE_DICTIONARY:
case DWG_TYPE_OLEFRAME:
case DWG_TYPE_MTEXT:
case DWG_TYPE_LEADER:
case DWG_TYPE_TOLERANCE:
return 1;
case DWG_TYPE_MLINE:
return 0;
case DWG_TYPE_BLOCK_CONTROL:
case DWG_TYPE_LAYER_CONTROL:
case DWG_TYPE_STYLE_CONTROL:
case DWG_TYPE_LTYPE_CONTROL:
case DWG_TYPE_VIEW_CONTROL:
case DWG_TYPE_UCS_CONTROL:
case DWG_TYPE_VPORT_CONTROL:
case DWG_TYPE_APPID_CONTROL:
case DWG_TYPE_DIMSTYLE_CONTROL:
case DWG_TYPE_VX_CONTROL:
return 0;
case DWG_TYPE_BLOCK_HEADER:
case DWG_TYPE_LAYER:
case DWG_TYPE_STYLE:
case DWG_TYPE_LTYPE:
case DWG_TYPE_VIEW:
case DWG_TYPE_UCS:
case DWG_TYPE_VPORT:
case DWG_TYPE_APPID:
case DWG_TYPE_DIMSTYLE:
case DWG_TYPE_VX_TABLE_RECORD:
return 1;
case DWG_TYPE_GROUP:
case DWG_TYPE_MLINESTYLE:
case DWG_TYPE_OLE2FRAME:
return 1;
case DWG_TYPE_DUMMY:
case DWG_TYPE_LONG_TRANSACTION:
case DWG_TYPE_LWPOLYLINE:
return 0;
case DWG_TYPE_HATCH:
case DWG_TYPE_XRECORD:
return 1;
case DWG_TYPE_PLACEHOLDER:
return 0;
case DWG_TYPE_VBA_PROJECT:
case DWG_TYPE_LAYOUT:
case DWG_TYPE_PROXY_ENTITY:
case DWG_TYPE_PROXY_OBJECT:
default:
return 1;
}
}
/* init and restrict the hdl_dat stream. */
int
obj_handle_stream (Bit_Chain *restrict dat, Dwg_Object *restrict obj,
Bit_Chain *restrict hdl_dat)
{
size_t bit8 = obj->bitsize / 8;
assert (dat != hdl_dat);
// The handle stream offset, i.e. end of the object, right after
// the has_strings bit.
obj->hdlpos = obj->bitsize; // relative to dat
// restrict it to 0-end
hdl_dat->byte = bit8;
hdl_dat->bit = obj->bitsize % 8;
// bit_reset_chain (hdl_dat); //but keep the same start
if (!obj->handlestream_size) // with strings we already did calc. it
{
obj->handlestream_size = (obj->size * 8) - obj->bitsize;
LOG_TRACE (" Hdlsize: " FORMAT_UMC ",", obj->handlestream_size);
}
// dat->size = bit8 + hdl_dat->bit ? 1 : 0;
hdl_dat->size = obj->size;
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_HANDLE)
{
size_t end = obj->bitsize + obj->handlestream_size;
LOG_HANDLE (
" hdl_dat: @%" PRIuSIZE ".%u - @%" PRIuSIZE ".%u (%" PRIuSIZE ")",
bit8, hdl_dat->bit, end / 8, (unsigned)(end % 8), hdl_dat->size);
}
LOG_TRACE ("\n");
return 0;
}
/* The first common part of every entity.
The last common part is common_entity_handle_data.spec
called by COMMON_ENTITY_HANDLE_DATA in dwg.spec
For EED check page 269, par 28 (Extended Object Data)
For proxy graphics check page 270, par 29 (Proxy Entity Graphics)
PRE(R_13b1) goes into decode_entity_preR13 instead.
*/
int
dwg_decode_entity (Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat,
Dwg_Object_Entity *restrict ent)
{
int error = 0;
Dwg_Data *dwg = ent->dwg;
Dwg_Object *obj = &dwg->object[ent->objid];
Dwg_Object_Entity *_obj = ent;
Dwg_Object_Entity *_ent = ent;
size_t objectpos = bit_position (dat);
int has_wrong_bitsize = 0;
// obj->dat_address = dat->byte; // the data stream offset
obj->bitsize_pos = objectpos; // absolute. needed for encode
VERSIONS (R_2000, R_2007)
{
obj->bitsize = bit_read_RL (dat); // until the handles
LOG_TRACE ("bitsize: " FORMAT_RL " [RL] @%" PRIuSIZE ".%u\n", obj->bitsize,
dat->byte - 2, dat->bit);
if (obj->bitsize > obj->size * 8)
{
LOG_ERROR ("Invalid bitsize " FORMAT_RL " > " FORMAT_RL, obj->bitsize,
obj->size * 8);
obj->bitsize = obj->size * 8;
has_wrong_bitsize = 1;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
}
else
error |= obj_handle_stream (dat, obj, hdl_dat);
}
SINCE (R_2007a)
{
SINCE (R_2010b)
{
LOG_HANDLE (" bitsize: " FORMAT_RL ",", obj->bitsize);
// restrict the hdl_dat stream
error |= obj_handle_stream (dat, obj, hdl_dat);
}
// and set the string stream (restricted to size)
// skip for all types without strings
if (obj->type >= 500 || obj_has_strings (obj->type))
error |= obj_string_stream (dat, obj, str_dat);
else
{
bit_set_position (str_dat, obj->bitsize - 1);
str_dat->size = 0;
}
}
error |= bit_read_H (dat, &(obj->handle));
if (error & DWG_ERR_INVALIDHANDLE || !obj->handle.value || !obj->handle.size)
{
LOG_ERROR ("Invalid object handle " FORMAT_H " at pos @%" PRIuSIZE ".%u",
ARGS_H (obj->handle), dat->byte, dat->bit);
// TODO reconstruct the handle and search in the bitsoup?
if (has_wrong_bitsize)
obj->bitsize = 0;
// obj->handle.value = 0;
// obj->handle.size = 0;
// obj->handle.code = 0;
ent->num_eed = 0;
ent->preview_exists = 0;
return error | DWG_ERR_INVALIDHANDLE;
}
LOG_TRACE ("handle: " FORMAT_H " [H 5]", ARGS_H (obj->handle));
LOG_INSANE (" @%" PRIuSIZE ".%u", dat->byte, dat->bit);
LOG_TRACE ("\n");
if (has_wrong_bitsize)
LOG_WARN ("Skip eed");
else
error |= dwg_decode_eed (dat, (Dwg_Object_Object *)ent);
LOG_INSANE (" @%" PRIuSIZE ".%u\n", dat->byte, dat->bit);
// LOG_TRACE ("\n");
if (error & (DWG_ERR_INVALIDEED | DWG_ERR_VALUEOUTOFBOUNDS))
return error;
// clang-format off
#include "common_entity_data.spec"
// clang-format on
dwg_decode_common_entity_handle_data (dat, hdl_dat, obj);
// elsewhere: object data, handles, padding bits, crc
obj->common_size = bit_position (dat) - objectpos;
LOG_HANDLE ("--common_size: %" PRIuSIZE "\n",
obj->common_size); // needed for unknown
return error;
}
/* The first common part of every object.
There is no COMMON_ENTITY_DATA for objects.
Check page 269, par 28 (Extended Object Data)
*/
int
dwg_decode_object (Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat,
Dwg_Object_Object *restrict _obj)
{
int error = 0;
Dwg_Data *dwg = _obj->dwg;
Dwg_Object *obj = &dwg->object[_obj->objid];
size_t objectpos = bit_position (dat);
int has_wrong_bitsize = 0; // first possibly fatal problem
BITCODE_BL vcount;
// obj->dat_address = dat->byte; // the data stream
obj->bitsize_pos = objectpos; // absolute. needed for encode
VERSIONS (R_2000, R_2007)
{
obj->bitsize = bit_read_RL (dat);
LOG_TRACE ("bitsize: " FORMAT_RL " [RL] @%" PRIuSIZE ".%u\n", obj->bitsize,
dat->byte - 2, dat->bit);
if (obj->bitsize > obj->size * 8)
{
LOG_ERROR ("Invalid bitsize " FORMAT_RL " > " FORMAT_RL, obj->bitsize,
obj->size * 8);
obj->bitsize = obj->size * 8;
has_wrong_bitsize = 1;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
}
else
error |= obj_handle_stream (dat, obj, hdl_dat);
}
SINCE (R_2007a)
{
SINCE (R_2010b)
{
LOG_HANDLE (" bitsize: " FORMAT_RL ",", obj->bitsize);
}
if (obj->bitsize > obj->size * 8)
{
obj->bitsize = obj->size * 8;
has_wrong_bitsize = 1;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
LOG_HANDLE (" (fixed)");
}
// restrict the hdl_dat stream. already done for r2007
SINCE (R_2010b)
{
error |= obj_handle_stream (dat, obj, hdl_dat);
}
// and set the string stream (restricted to size)
if (obj->type >= 500 || obj_has_strings (obj->type))
error |= obj_string_stream (dat, obj, str_dat);
else
{
str_dat->chain += str_dat->byte;
str_dat->byte = 0;
str_dat->bit = 0;
bit_advance_position (str_dat, obj->bitsize - 1 - 8);
str_dat->size = 0;
}
}
SINCE (R_13b1)
{
error |= bit_read_H (dat, &obj->handle);
if (error & DWG_ERR_INVALIDHANDLE || !obj->handle.value
|| !obj->handle.size || obj->handle.code)
{
LOG_ERROR ("Invalid object handle " FORMAT_H " at pos @%" PRIuSIZE
".%u",
ARGS_H (obj->handle), dat->byte, dat->bit);
// TODO reconstruct the handle and search in the bitsoup?
if (has_wrong_bitsize)
obj->bitsize = 0;
obj->tio.object->num_eed = 0;
return error | DWG_ERR_INVALIDHANDLE;
}
LOG_TRACE ("handle: " FORMAT_H " [H 5]\n", ARGS_H (obj->handle));
}
SINCE (R_13b1)
{
if (has_wrong_bitsize)
LOG_WARN ("Skip eed");
else
error |= dwg_decode_eed (dat, _obj);
if (error & (DWG_ERR_INVALIDEED | DWG_ERR_VALUEOUTOFBOUNDS))
return error;
}
VERSIONS (R_13b1, R_14)
{
obj->bitsize = bit_read_RL (dat);
LOG_TRACE ("bitsize: %u [RL]\n", obj->bitsize);
if (obj->bitsize > obj->size * 8)
{
LOG_ERROR ("Invalid bitsize " FORMAT_RL " > " FORMAT_RL, obj->bitsize,
obj->size * 8);
obj->bitsize = obj->size * 8;
has_wrong_bitsize = 1;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
}
else
error |= obj_handle_stream (dat, obj, hdl_dat);
}
// clang-format off
#include "common_object_handle_data.spec"
// clang-format on
obj->common_size = bit_position (dat) - objectpos;
LOG_HANDLE ("--common_size: %" PRIuSIZE "\n",
obj->common_size); // needed for unknown
return error;
}
static int
dwg_decode_add_object_ref (Dwg_Data *restrict dwg, Dwg_Object_Ref *ref)
{
Dwg_Object_Ref **object_ref_old = dwg->object_ref;
// Reserve memory space for object references
if (!dwg->num_object_refs)
{
BITCODE_RLL max_refs = dwg->header_vars.HANDSEED
? dwg->header_vars.HANDSEED->absolute_ref
: REFS_PER_REALLOC;
if (max_refs < REFS_PER_REALLOC)
max_refs = REFS_PER_REALLOC;
dwg->object_ref
= (Dwg_Object_Ref **)calloc (max_refs, sizeof (Dwg_Object_Ref *));
}
else if (dwg->num_object_refs % REFS_PER_REALLOC == 0)
{
dwg->object_ref = (Dwg_Object_Ref **)realloc (
dwg->object_ref, (dwg->num_object_refs + REFS_PER_REALLOC)
* sizeof (Dwg_Object_Ref *));
memset (&dwg->object_ref[dwg->num_object_refs], 0,
REFS_PER_REALLOC * sizeof (Dwg_Object_Ref *));
// The realloc moves only the array of pointers; the refs and their
// cached ref->obj are untouched, so dirty_refs is not set here.
LOG_TRACE ("REALLOC dwg->object_ref vector to %u\n",
dwg->num_object_refs + REFS_PER_REALLOC);
}
if (!dwg->object_ref)
{
LOG_ERROR ("Out of memory");
dwg->object_ref = object_ref_old;
return DWG_ERR_OUTOFMEM;
}
dwg->object_ref[dwg->num_object_refs++] = ref;
ref->handleref.is_global = 1;
return 0;
}
/* Returns a freshly allocated ref */
EXPORT Dwg_Object_Ref *
dwg_new_ref (Dwg_Data *restrict dwg)
{
Dwg_Object_Ref *ref = (Dwg_Object_Ref *)calloc (1, sizeof (Dwg_Object_Ref));
if (!ref)
{
LOG_ERROR ("Out of memory");
return NULL;
}
if (dwg_decode_add_object_ref (dwg, ref))
{
free (ref);
LOG_INSANE ("dwg_decode_add_object_ref failed " FORMAT_BL "\n",
dwg->num_object_refs);
return NULL;
}
return ref;
}
/* Store an object reference in a separate dwg->object_ref array
which is the id for handles, i.e. DXF 5, 330. */
Dwg_Object_Ref *
dwg_decode_handleref (Bit_Chain *restrict dat, Dwg_Object *restrict obj,
Dwg_Data *restrict dwg)
{
// Welcome to the house of evil code
Dwg_Object_Ref *ref = (Dwg_Object_Ref *)calloc (1, sizeof (Dwg_Object_Ref));
if (!ref)
{
LOG_ERROR ("Out of memory");
return NULL;
}
if (bit_read_H (dat, &ref->handleref))
{
free (ref);
return NULL;
}
// If the handle size is 0 and not a relative handle, it is probably a null
// handle. It shouldn't be placed in the object ref vector.
if (ref->handleref.size || (obj && ref->handleref.code > 5))
{
if (dwg_decode_add_object_ref (dwg, ref))
{
free (ref);
return NULL;
}
}
else if (!ref->handleref.value)
{
if (obj)
{
free (ref);
return NULL;
}
ref->absolute_ref = 0;
ref->obj = NULL;
return ref;
}
// We receive a null obj when we are reading
// handles in the header variables section
if (!obj)
{
if (ref->handleref.value)
{
ref->absolute_ref = ref->handleref.value;
ref->obj = NULL;
return ref;
}
if (ref->handleref.code >= 6)
{
LOG_ERROR ("Empty obj argument for handleref code %d",
ref->handleref.code);
ref->obj = NULL;
return NULL;
}
}
/*
* With TYPEDOBJHANDLE 2-5 the code indicates the type of ownership.
* With OFFSETOBJHANDLE >5 the handle is stored as an offset from some other
* handle.
*/
switch (ref->handleref.code)
{
case 6:
ref->absolute_ref = (obj->handle.value + 1);
break;
case 8:
ref->absolute_ref = (obj->handle.value - 1);
break;
case 10:
ref->absolute_ref = (obj->handle.value + ref->handleref.value);
break;
case 12:
ref->absolute_ref = (obj->handle.value - ref->handleref.value);
break;
case 14: // eg 2007 REGION.history_id (some very high number)
ref->absolute_ref = obj->handle.value;
break;
case 2:
case 3:
case 4:
case 5:
ref->absolute_ref = ref->handleref.value;
break;
case 0: // ignore?
ref->absolute_ref = ref->handleref.value;
break;
default:
// dwg->object_ref[dwg->num_object_refs-1] = NULL;
// dwg->num_object_refs--;
ref->absolute_ref = 0;
ref->obj = NULL;
LOG_WARN ("Invalid handle pointer code %d", ref->handleref.code);
break;
}
return ref;
}
/**
* code:
* TYPEDOBJHANDLE:
* 2 Soft owner
* 3 Hard owner
* 4 Soft pointer
* 5 Hard pointer
* OFFSETOBJHANDLE for soft owners or pointers:
* 6 ref + 1
* 8 ref - 1
* a ref + offset
* c ref - offset
* e ??
*/
Dwg_Object_Ref *
dwg_decode_handleref_with_code (Bit_Chain *restrict dat,
Dwg_Object *restrict obj,
Dwg_Data *restrict dwg, unsigned int code)
{
Dwg_Object_Ref *ref = (Dwg_Object_Ref *)calloc (1, sizeof (Dwg_Object_Ref));
if (!ref)
{
LOG_ERROR ("Out of memory");
return NULL;
}
if (bit_read_H (dat, &ref->handleref))
{
free (ref);
return NULL;
}
/* If the handle size is 0 and not a relative handle, it is probably
a null handle.
It shouldn't be placed in the object ref vector,
just with indxf the NULL HDL is. */
if (ref->handleref.size || (obj && ref->handleref.code > 5))
{
if (dwg_decode_add_object_ref (dwg, ref))
{
free (ref);
return NULL;
}
}
else if (!ref->handleref.value)
{
/*if (obj)
{
free (ref);
return NULL;
}
*/
ref->absolute_ref = 0;
ref->obj = NULL;
return ref;
}
// We receive a null obj when we are reading
// handles in the header variables section
if (!obj)
{
ref->absolute_ref = ref->handleref.value;
ref->obj = NULL;
return ref;
}
/*
* With TYPEDOBJHANDLE 2-5 the code indicates the type of ownership.
* With OFFSETOBJHANDLE >5 the handle is stored as an offset from some other
* handle.
*/
switch (ref->handleref.code)
{
case 0x06:
ref->absolute_ref = (obj->handle.value + 1);
break;
case 0x08:
ref->absolute_ref = (obj->handle.value - 1);
break;
case 0x0A:
ref->absolute_ref = (obj->handle.value + ref->handleref.value);
break;
case 0x0C:
ref->absolute_ref = (obj->handle.value - ref->handleref.value);
break;
case 0x0E: // eg 2007 REGION.history_id (some very high number)
ref->absolute_ref = obj->handle.value;
break;
case 2:
case 3:
case 4:
case 5:
ref->absolute_ref = ref->handleref.value;
break;
case 0: // ignore?
ref->absolute_ref = ref->handleref.value;
break;
default:
// dwg->object_ref[dwg->num_object_refs-1] = NULL;
// dwg->num_object_refs--;
ref->absolute_ref = 0;
ref->obj = NULL;
LOG_WARN ("Invalid handle pointer code %d", ref->handleref.code);
break;
}
return ref;
}
AFL_GCC_TOOBIG
int
dwg_decode_header_variables (Bit_Chain *dat, Bit_Chain *hdl_dat,
Bit_Chain *str_dat, Dwg_Data *restrict dwg)
{
Dwg_Header_Variables *_obj = &dwg->header_vars;
Dwg_Object *obj = NULL;
int error = 0;
// clang-format off
#include "header_variables.spec"
// clang-format on
if (!dwg->header_vars.BLOCK_RECORD_MSPACE)
return error | DWG_ERR_INVALIDDWG;
return error;
}
AFL_GCC_POP
static int
dwg_decode_common_entity_handle_data (Bit_Chain *dat, Bit_Chain *hdl_dat,
Dwg_Object *restrict obj)
{
Dwg_Data *dwg = obj->parent;
Dwg_Object_Entity *_obj, *_ent;
BITCODE_BL vcount;
int error = 0;
_obj = _ent = obj->tio.entity;
// clang-format off
#include "common_entity_handle_data.spec"
// clang-format on
return error;
}
void
dwg_free_xdata_resbuf (Dwg_Resbuf *rbuf)
{
while (rbuf)
{
Dwg_Resbuf *next = rbuf->nextrb;
short type = dwg_resbuf_value_type (rbuf->type);
if (type == DWG_VT_STRING || type == DWG_VT_BINARY)
free (rbuf->value.str.u.data);
free (rbuf);
rbuf = next;
}
}
// TODO: unify with eed[], use an array not linked list.
Dwg_Resbuf *
dwg_decode_xdata (Bit_Chain *restrict dat, Dwg_Object_XRECORD *restrict obj,
BITCODE_BL xdata_size)
{
Dwg_Resbuf *rbuf, *root = NULL, *curr = NULL;
size_t start_address, end_address, curr_address;
BITCODE_BL i, num_xdata = 0;
BITCODE_RS length;
start_address = dat->byte;
end_address = start_address + (size_t)xdata_size;
if (obj->parent && obj->parent->objid)
{
Dwg_Data *dwg = obj->parent->dwg;
Dwg_Object *o = &dwg->object[obj->parent->objid];
if (end_address > o->size)
{
LOG_WARN ("Invalid XRECORD.xdata_size " FORMAT_BL, xdata_size);
if ((o->bitsize / 8) <= (BITCODE_RL)start_address)
{
// fuzzers only
LOG_ERROR ("No room for xdata size:" FORMAT_RL
" <= pos:" FORMAT_RL,
(o->bitsize / 8), (BITCODE_RL)start_address);
obj->xdata_size = 0;
return NULL;
}
// Possible ACAD bug. Calculate by ourselves
xdata_size = (BITCODE_BL)((o->bitsize / 8) - start_address);
LOG_INFO ("calc xdata_size => " FORMAT_BL "\n", xdata_size);
obj->xdata_size = xdata_size;
end_address = start_address + (size_t)xdata_size;
}
}
if (DWG_LOGLEVEL >= DWG_LOGLEVEL_INSANE)
{
LOG_INSANE ("XDATA size: " FORMAT_BL " (end @%" PRIuSIZE ")\n",
xdata_size, end_address);
LOG_INSANE_TF (&dat->chain[dat->byte], (int)xdata_size);
}
curr_address = dat->byte;
while (dat->byte < end_address)
{
enum RESBUF_VALUE_TYPE vtype;
rbuf = (Dwg_Resbuf *)calloc (1, sizeof (Dwg_Resbuf));
if (!rbuf)
{
LOG_ERROR ("Out of memory");
dwg_free_xdata_resbuf (root);
return NULL;
}
rbuf->nextrb = NULL;
rbuf->type = bit_read_RS (dat);
LOG_HANDLE (" xdata[%u] type: " FORMAT_RS " [RS]\n", num_xdata,
rbuf->type);
if (dat->byte == curr_address || dat->byte >= end_address)
{
// no advance, by dat overflow
free (rbuf);
dat->byte = end_address;
break;
}
if (rbuf->type < 0 || rbuf->type >= 2000)
{
LOG_ERROR ("Invalid xdata type %d [RS]", rbuf->type);
free (rbuf);
dat->byte = end_address;
break;
}
vtype = dwg_resbuf_value_type (rbuf->type);
switch (vtype)
{
case DWG_VT_STRING:
PRE (R_2007a)
{
length = bit_read_RS (dat);
LOG_INSANE ("xdata[%u]: length " FORMAT_RS "\n", num_xdata,
length);
rbuf->value.str.codepage = bit_read_RC (dat);
LOG_INSANE ("xdata[%u]: codepage %u\n", num_xdata,
(unsigned)rbuf->value.str.codepage);
if (dat->byte + length > end_address || (short)length < 0)
break;
rbuf->value.str.is_tu = 0;
rbuf->value.str.size = length;
rbuf->value.str.u.data = (char *)bit_read_TF (dat, length);
LOG_INSANE ("STRING ");
LOG_TRACE ("xdata[%u]: \"%s\" [TV %d]\n", num_xdata,
rbuf->value.str.u.data, rbuf->type);
}
LATER_VERSIONS
{
length = bit_read_RS (dat);
if ((short)length > 0 && dat->byte + (length * 2) <= end_address)
{
rbuf->value.str.u.wdata = (BITCODE_TU)calloc (length + 1, 2);
if (!rbuf->value.str.u.wdata)
{
LOG_ERROR ("Out of memory");
obj->xdata_size = 0;
obj->num_xdata = 0;
if (root)
{
dwg_free_xdata_resbuf (root);
if (rbuf)
free (rbuf);
}
else
dwg_free_xdata_resbuf (rbuf);
return NULL;
}
rbuf->value.str.is_tu = 1;
rbuf->value.str.size = length;
for (i = 0; i < length; i++)
rbuf->value.str.u.wdata[i] = bit_read_RS (dat);
rbuf->value.str.u.wdata[i] = '\0';
LOG_INSANE ("UNISTRING ");
LOG_TRACE_TU ("xdata", rbuf->value.str.u.wdata, rbuf->type);
}
else
{
rbuf->value.str.is_tu = 1;
rbuf->value.str.size = 0;
}
}
break;
case DWG_VT_REAL:
if (dat->byte + 8 > end_address)
break;
rbuf->value.dbl = bit_read_RD (dat);
LOG_TRACE ("xdata[%u]: %f [RD %d]\n", num_xdata, rbuf->value.dbl,
rbuf->type);
break;
case DWG_VT_BOOL:
case DWG_VT_INT8:
if (dat->byte + 1 > end_address)
break;
rbuf->value.i8 = bit_read_RC (dat);
LOG_TRACE ("xdata[%u]: %d [RC %d]\n", num_xdata, (int)rbuf->value.i8,
rbuf->type);
break;
case DWG_VT_INT16:
if (dat->byte + 2 > end_address)
break;
rbuf->value.i16 = (short)bit_read_RS (dat);
LOG_TRACE ("xdata[%u]: %d [RS %d]\n", num_xdata,
(int)rbuf->value.i16, rbuf->type);
break;
case DWG_VT_INT32:
if (dat->byte + 4 > end_address)
break;
rbuf->value.i32 = (int32_t)bit_read_RL (dat);
LOG_TRACE ("xdata[%u]: %d [RLd %d]\n", num_xdata,
(int)rbuf->value.i32, rbuf->type);
break;
case DWG_VT_INT64:
if (dat->byte + 8 > end_address)
break;
rbuf->value.i64 = (int64_t)bit_read_RLL (dat);
LOG_TRACE ("xdata[%u]: " FORMAT_RLLd " [RLLd %d]\n", num_xdata,
rbuf->value.i64, rbuf->type);
break;
case DWG_VT_POINT3D:
if (dat->byte + 24 > end_address)
break;
rbuf->value.pt[0] = bit_read_RD (dat);
rbuf->value.pt[1] = bit_read_RD (dat);
rbuf->value.pt[2] = bit_read_RD (dat);
LOG_TRACE ("xdata[%u]: %f,%f,%f [3RD %d]\n", num_xdata,
rbuf->value.pt[0], rbuf->value.pt[1], rbuf->value.pt[2],
rbuf->type);
break;
case DWG_VT_BINARY:
rbuf->value.str.size = bit_read_RC (dat);
if (dat->byte + rbuf->value.str.size > end_address)
{
LOG_ERROR ("Invalid XDATA BINARY size %u",
(unsigned)rbuf->value.str.size);
rbuf->value.str.size = 0;
break;
}
rbuf->value.str.u.data
= (char *)bit_read_TF (dat, rbuf->value.str.size);
LOG_INSANE ("BINARY ");
LOG_TRACE ("xdata[%u]: [TF %d %d] ", num_xdata, rbuf->value.str.size,
rbuf->type);
LOG_TRACE_TF (rbuf->value.str.u.data, rbuf->value.str.size);
break;
case DWG_VT_HANDLE:
case DWG_VT_OBJECTID:
if (dat->byte + 8 > end_address)
break;
rbuf->value.absref = bit_read_RLL (dat);
LOG_TRACE ("xdata[%u]: " FORMAT_HV " [H %d]\n", num_xdata,
rbuf->value.absref, rbuf->type);
break;
case DWG_VT_INVALID:
default:
LOG_ERROR ("Invalid group code in xdata[%u]: %d", num_xdata,
rbuf->type);
LOG_WARN ("xdata read %" PRIuSIZE ", expected %d",
dat->byte - start_address, obj->xdata_size);
dwg_free_xdata_resbuf (rbuf);
if (curr)
curr->nextrb = NULL;
dat->byte = end_address;
obj->num_xdata = num_xdata;
return root;
}
num_xdata++;
if (!curr)
{
curr = root = rbuf;
}
else
{
curr->nextrb = rbuf;
curr = rbuf;
}
curr_address = dat->byte;
}
if (dat->byte - start_address != obj->xdata_size)
LOG_WARN ("xdata read %" PRIuSIZE ", expected %d",
dat->byte - start_address, obj->xdata_size);
obj->num_xdata = num_xdata;
return root;
}
static BITCODE_BB
bit_read_BB_noadv (Bit_Chain *dat)
{
unsigned char result;
unsigned char byte;
if (dat->byte >= dat->size)
{
LOG_ERROR ("%s buffer overflow at %" PRIuSIZE " >= %" PRIuSIZE,
__FUNCTION__, dat->byte, dat->size);
return 9;
}
byte = dat->chain[dat->byte];
if (dat->bit < 7)
result = (byte & (0xc0 >> dat->bit)) >> (6 - dat->bit);
else
{
result = (byte & 0x01) << 1;
if (dat->byte < dat->size - 1)
{
byte = dat->chain[dat->byte + 1];
result |= (byte & 0x80) >> 7;
}
}
return (BITCODE_BB)result;
}
/* OBJECTS *******************************************************************/
#include "dwg.spec"
/* Forward declarations for functions generated from dwg2.spec in decode2.c.
Uses objects.inc which also covers first-half (static) functions, so
suppress the redundant-decls warning for those. */
#undef DWG_ENTITY
#undef DWG_ENTITY_END
#undef DWG_OBJECT
#undef DWG_OBJECT_END
GCC46_DIAG_IGNORE (-Wredundant-decls)
#define DWG_ENTITY(token) \
extern int dwg_decode_##token (Bit_Chain *restrict dat, \
Dwg_Object *restrict obj);
#define DWG_ENTITY_END
#define DWG_OBJECT(token) \
extern int dwg_decode_##token (Bit_Chain *restrict dat, \
Dwg_Object *restrict obj);
#define DWG_OBJECT_END
#include "objects.inc"
GCC46_DIAG_RESTORE
/*--------------------------------------------------------------------------------
* Private functions which depend on the preceding
*/
/* check the types of all referenced handles.
r2010+ often mix up the hdlstream offset:
layer,vertex*,seqend. check the types then also.
*/
static void
check_POLYLINE_handles (Dwg_Object *obj)
{
/* We ensured the common fields structure is shared with all 4 types */
Dwg_Entity_POLYLINE_2D *_obj = obj->tio.entity->tio.POLYLINE_2D;
Dwg_Data *dwg = obj->parent;
if (dwg->header.version >= R_2004)
{
BITCODE_BL i = 0;
Dwg_Object_Ref *layer = obj->tio.entity->layer;
Dwg_Object_Ref *seqend = _obj->seqend;
// resolve prev. object
if (layer && !layer->obj)
layer->obj = dwg_ref_object_relative (dwg, layer, obj);
if (!layer || !layer->obj)
{ // maybe a reactor pointing forwards or vertex
LOG_WARN ("Wrong POLYLINE.layer " FORMAT_HV "",
layer ? layer->handleref.value : 0L);
if (_obj->num_owned > 0 && _obj->vertex)
{
Dwg_Object_Ref *vertex = _obj->vertex[0];
if (vertex && !vertex->obj)
vertex->obj = dwg_ref_object_relative (dwg, vertex, obj);
if (vertex && vertex->obj /* pointing backwards */
&& vertex->obj->fixedtype == DWG_TYPE_LAYER)
{
Dwg_Object *seq;
obj->tio.entity->layer = layer = vertex;
LOG_WARN ("POLYLINE.layer is vertex[0] " FORMAT_HV
", shift em, NULL seqend",
layer->handleref.value);
/* shift vertices one back */
for (i = 0; i < _obj->num_owned - 1; i++)
{
_obj->vertex[i] = _obj->vertex[i + 1];
}
_obj->vertex[_obj->num_owned - 1] = seqend;
_obj->seqend = NULL;
/* now just seqend is empty.
either 1+ last_vertex, or one before the first.
Here the next object might not be read yet. */
seq = dwg_next_object (obj);
if (seq && seq->type == DWG_TYPE_SEQEND)
{
LOG_WARN ("POLYLINE.seqend = POLYLINE+1 " FORMAT_HV "",
seq->handle.value);
seqend = _obj->seqend = dwg_find_objectref (dwg, seq);
}
else
{
seq = seqend ? dwg_next_object (seqend->obj) : NULL;
if (seq && seq->type == DWG_TYPE_SEQEND)
{
LOG_WARN ("POLYLINE.seqend = VERTEX+1 " FORMAT_HV "",
seq->handle.value);
seqend = _obj->seqend
= dwg_find_objectref (dwg, seq);
}
}
}
}
}
else if (layer->obj->fixedtype != DWG_TYPE_LAYER
&& layer->obj->fixedtype != DWG_TYPE_DICTIONARY)
{ // a vertex would be after, a reactor before
LOG_WARN ("Wrong POLYLINE.layer %s", layer->obj->dxfname);
}
// a SEQEND is always after the polyline, so it cannot be resolved yet
if (!seqend || !seqend->handleref.value)
{
LOG_WARN ("Empty POLYLINE.seqend");
}
else if (seqend->obj && seqend->obj->fixedtype != DWG_TYPE_SEQEND)
{ // most likely a vertex, off by one
LOG_WARN ("Wrong POLYLINE.seqend %s", seqend->obj->dxfname);
}
if (!_obj->vertex)
return;
for (; i < _obj->num_owned; i++)
{
Dwg_Object_Ref *v = _obj->vertex[i];
if (!v || !v->handleref.value)
{
LOG_WARN ("Empty POLYLINE.vertex[%d]", i);
}
else if (v->obj && v->obj->fixedtype != DWG_TYPE_VERTEX_2D
&& v->obj->fixedtype != DWG_TYPE_VERTEX_3D
&& v->obj->fixedtype != DWG_TYPE_VERTEX_MESH
&& v->obj->fixedtype != DWG_TYPE_VERTEX_PFACE
&& v->obj->fixedtype != DWG_TYPE_VERTEX_PFACE_FACE)
{
LOG_WARN ("Wrong POLYLINE.vertex[%d] " FORMAT_HV " %s", i,
v->handleref.value, v->obj->dxfname);
}
}
}
}
/** dwg_decode_variable_type
* decode object by class name, not type. if type > 500.
* returns 0 on success
*/
static int
dwg_decode_variable_type (Dwg_Data *restrict dwg, Bit_Chain *dat,
Bit_Chain *hdl_dat, Dwg_Object *restrict obj)
{
Dwg_Class *klass;
int i;
int is_entity;
if (!dwg || !obj || !dat)
return DWG_ERR_INTERNALERROR;
i = obj->type - 500;
if (i < 0 || i >= dwg->num_classes)
{
LOG_ERROR ("Invalid object type %d, only %u classes", obj->type,
dwg->num_classes);
dwg->num_objects--;
return DWG_ERR_UNHANDLEDCLASS;
}
klass = &dwg->dwg_class[i];
if (!dwg->dwg_class || !klass->dxfname)
{
LOG_ERROR ("Invalid class %d", obj->type);
return DWG_ERR_UNHANDLEDCLASS;
}
obj->dxfname = klass->dxfname;
is_entity = dwg_class_is_entity (klass);
// clang-format off
// global class dispatcher
#include "classes.inc"
// clang-format on
LOG_WARN ("Unknown Class %s %d %s (0x%x%s)", is_entity ? "entity" : "object",
klass->number, klass->dxfname, klass->proxyflag,
klass->is_zombie ? "is_zombie" : "");
return DWG_ERR_UNHANDLEDCLASS;
}
/** Adds a new empty obj to the dwg->object[] array.
The new object is at &dwg->object[dwg->num_objects-1].
Returns 0 or some error codes on success.
Returns -1 if the dwg->object pool was re-alloced, i.e. pointers within
are invalidated.
Returns DWG_ERR_OUTOFMEM otherwise.
*/
EXPORT int
dwg_add_object (Dwg_Data *restrict dwg)
{
Dwg_Object *restrict obj;
BITCODE_BL num = dwg->num_objects;
int realloced = 0;
loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
if (num && !dwg->num_alloced_objects)
dwg->num_alloced_objects = num;
if (!num && !dwg->object)
{
dwg->object = (Dwg_Object *)calloc (1024, sizeof (Dwg_Object));
dwg->num_alloced_objects = 1024;
dwg->dirty_refs = 0;
}
else if (num >= dwg->num_alloced_objects)
{
Dwg_Object *restrict old = dwg->object;
BITCODE_BL old_num = dwg->num_alloced_objects;
if (!dwg->num_alloced_objects)
dwg->num_alloced_objects = 1;
while (num >= dwg->num_alloced_objects)
dwg->num_alloced_objects *= 2;
dwg->object = (Dwg_Object *)realloc (
dwg->object, dwg->num_alloced_objects * sizeof (Dwg_Object));
realloced = old != dwg->object;
if (realloced)
{
dwg->dirty_refs = 1;
memset (&dwg->object[old_num], 0,
(dwg->num_alloced_objects - old_num) * sizeof (Dwg_Object));
LOG_TRACE ("REALLOC dwg->object vector to %u\n",
dwg->num_alloced_objects);
}
}
if (!dwg->object)
return DWG_ERR_OUTOFMEM;
obj = &dwg->object[num];
memset (obj, 0, sizeof (Dwg_Object));
obj->index = num;
dwg->num_objects++;
obj->parent = dwg;
return realloced ? -1 : 0;
}
/** Adds an object to the DWG (i.e. dwg->object[dwg->num_objects])
Returns 0 or some error codes on success.
Returns -1 if the dwg->object pool was re-alloced.
Returns some DWG_ERR_* otherwise.
*/
int
dwg_decode_add_object (Dwg_Data *restrict dwg, Bit_Chain *dat,
Bit_Chain *hdl_dat, size_t address)
{
size_t objpos, restartpos;
Bit_Chain abs_dat = { 0 };
Dwg_Object *restrict obj;
BITCODE_BL num = dwg->num_objects;
int error = 0;
int realloced = 0;
/* Keep the previous full chain */
abs_dat = *dat;
/* Use the indicated address for the object
*/
dat->byte = address;
dat->bit = 0;
// DEBUG_HERE;
/*
* Reserve memory space for objects. A realloc violates all internal
* pointers.
*/
realloced = dwg_add_object (dwg);
if (realloced > 0) // i.e. not realloced, but error
{
*dat = abs_dat;
return realloced; // i.e. DWG_ERR_OUTOFMEM
}
obj = &dwg->object[num];
LOG_INFO ("==========================================\n"
"Object number: %lu/%lX",
(unsigned long)num, (unsigned long)num);
if (dat->byte >= dat->size)
{
LOG_ERROR ("MS size overflow @%" PRIuSIZE, dat->byte);
dwg->num_objects--;
*dat = abs_dat;
return DWG_ERR_VALUEOUTOFBOUNDS;
}
// #ifdef DEBUG
// if ((dat->opts & DWG_OPTS_LOGLEVEL) > 5 && obj->index == 12)
// bit_explore_chain (dat, dat->byte, 16);
// #endif
obj->size = bit_read_MS (dat);
LOG_INFO (", Size: %d [MS]", obj->size);
SINCE (R_2010b)
{
/* This is not counted in the object size */
obj->handlestream_size = bit_read_UMC (dat);
LOG_INFO (", Hdlsize: " FORMAT_UMC " [UMC] ", obj->handlestream_size);
obj->bitsize = obj->size * 8 - obj->handlestream_size;
// TODO boundscheck
}
objpos = bit_position (dat); // absolute
obj->address = dat->byte;
/* Until here dat is absolute. now restrict it */
bit_reset_chain (dat);
if (obj->size > dat->size || dat->size > abs_dat.size
|| &dat->chain[dat->size] < &dat->chain[0]
|| &abs_dat.chain[abs_dat.size] < &abs_dat.chain[0]
|| &dat->chain[dat->size] > &abs_dat.chain[abs_dat.size])
{
LOG_TRACE ("\n");
LOG_WARN ("Invalid object size %u > %" PRIuSIZE ". Would overflow",
obj->size, dat->size);
dwg->num_objects--;
error |= DWG_ERR_VALUEOUTOFBOUNDS;
#if 0
obj->size = dat->size - 1;
#else
*dat = abs_dat;
return error;
#endif
}
dat->size = obj->size;
SINCE (R_2010b)
{
obj->type = bit_read_BOT (dat);
}
else
{
obj->type = bit_read_BS (dat);
}
LOG_INFO (", Type: %d [%s]", obj->type,
dat->from_version >= R_2010 ? "BOT" : "BS");
LOG_HANDLE (", Address: %" PRIuSIZE, obj->address);
LOG_INFO ("\n");
restartpos = bit_position (dat); // relative
/* Check the type of the object
*/
switch (obj->type)
{
case DWG_TYPE_TEXT:
error = dwg_decode_TEXT (dat, obj);
break;
case DWG_TYPE_ATTRIB:
error = dwg_decode_ATTRIB (dat, obj);
break;
case DWG_TYPE_ATTDEF:
error = dwg_decode_ATTDEF (dat, obj);
break;
case DWG_TYPE_BLOCK:
error = dwg_decode_BLOCK (dat, obj);
break;
case DWG_TYPE_ENDBLK:
error = dwg_decode_ENDBLK (dat, obj);
break;
case DWG_TYPE_SEQEND:
error = dwg_decode_SEQEND (dat, obj);
if (dat->from_version >= R_13b1 && obj->tio.entity->ownerhandle)
{
Dwg_Object *restrict owner = dwg_resolve_handle (
dwg, obj->tio.entity->ownerhandle->absolute_ref);
if (!owner)
{
LOG_WARN ("no SEQEND.ownerhandle");
}
else if (owner->fixedtype == DWG_TYPE_INSERT
|| owner->fixedtype == DWG_TYPE_ACIDBLOCKREFERENCE
|| owner->fixedtype == DWG_TYPE_MINSERT)
{
/* SEQEND handle for the owner needed in validate_INSERT */
hash_set (dwg->object_map, obj->handle.value, (uint64_t)num);
(void)dwg_validate_INSERT (owner);
}
else if (owner->fixedtype == DWG_TYPE_POLYLINE_2D
|| owner->fixedtype == DWG_TYPE_POLYLINE_3D
|| owner->fixedtype == DWG_TYPE_POLYLINE_PFACE
|| owner->fixedtype == DWG_TYPE_POLYLINE_MESH)
{
Dwg_Entity_POLYLINE_2D *restrict _obj
= owner->tio.entity->tio.POLYLINE_2D;
if (!_obj->seqend)
/* SEQEND handle for the owner needed in validate_POLYLINE */
hash_set (dwg->object_map, obj->handle.value, (uint64_t)num);
(void)dwg_validate_POLYLINE (owner);
}
}
break;
case DWG_TYPE_INSERT:
error = dwg_decode_INSERT (dat, obj);
break;
case DWG_TYPE_MINSERT:
error = dwg_decode_MINSERT (dat, obj);
break;
case DWG_TYPE_VERTEX_2D:
error = dwg_decode_VERTEX_2D (dat, obj);
break;
case DWG_TYPE_VERTEX_3D:
error = dwg_decode_VERTEX_3D (dat, obj);
break;
case DWG_TYPE_VERTEX_MESH:
error = dwg_decode_VERTEX_MESH (dat, obj);
break;
case DWG_TYPE_VERTEX_PFACE:
error = dwg_decode_VERTEX_PFACE (dat, obj);
break;
case DWG_TYPE_VERTEX_PFACE_FACE:
error = dwg_decode_VERTEX_PFACE_FACE (dat, obj);
break;
case DWG_TYPE_POLYLINE_2D:
error = dwg_decode_POLYLINE_2D (dat, obj);
if (dat->from_version >= R_2010 && error < DWG_ERR_CRITICAL)
check_POLYLINE_handles (obj);
break;
case DWG_TYPE_POLYLINE_3D:
error = dwg_decode_POLYLINE_3D (dat, obj);
if (dat->from_version >= R_2010 && error < DWG_ERR_CRITICAL)
check_POLYLINE_handles (obj);
break;
case DWG_TYPE_ARC:
error = dwg_decode_ARC (dat, obj);
break;
case DWG_TYPE_CIRCLE:
error = dwg_decode_CIRCLE (dat, obj);
break;
case DWG_TYPE_LINE:
error = dwg_decode_LINE (dat, obj);
break;
case DWG_TYPE_DIMENSION_ORDINATE:
error = dwg_decode_DIMENSION_ORDINATE (dat, obj);
break;
case DWG_TYPE_DIMENSION_LINEAR:
error = dwg_decode_DIMENSION_LINEAR (dat, obj);
break;
case DWG_TYPE_DIMENSION_ALIGNED:
error = dwg_decode_DIMENSION_ALIGNED (dat, obj);
break;
case DWG_TYPE_DIMENSION_ANG3PT:
error = dwg_decode_DIMENSION_ANG3PT (dat, obj);
break;
case DWG_TYPE_DIMENSION_ANG2LN:
error = dwg_decode_DIMENSION_ANG2LN (dat, obj);
break;
case DWG_TYPE_DIMENSION_RADIUS:
error = dwg_decode_DIMENSION_RADIUS (dat, obj);
break;
case DWG_TYPE_DIMENSION_DIAMETER:
error = dwg_decode_DIMENSION_DIAMETER (dat, obj);
break;
case DWG_TYPE_POINT:
error = dwg_decode_POINT (dat, obj);
break;
case DWG_TYPE__3DFACE:
error = dwg_decode__3DFACE (dat, obj);
break;
case DWG_TYPE_POLYLINE_PFACE:
error = dwg_decode_POLYLINE_PFACE (dat, obj);
if (dat->from_version >= R_2010 && error < DWG_ERR_CRITICAL)
check_POLYLINE_handles (obj);
break;
case DWG_TYPE_POLYLINE_MESH:
error = dwg_decode_POLYLINE_MESH (dat, obj);
if (dat->from_version >= R_2010)
check_POLYLINE_handles (obj);
break;
case DWG_TYPE_SOLID:
error = dwg_decode_SOLID (dat, obj);
break;
case DWG_TYPE_TRACE:
error = dwg_decode_TRACE (dat, obj);
break;
case DWG_TYPE_SHAPE:
error = dwg_decode_SHAPE (dat, obj);
break;
case DWG_TYPE_VIEWPORT:
error = dwg_decode_VIEWPORT (dat, obj);
break;
case DWG_TYPE_ELLIPSE:
error = dwg_decode_ELLIPSE (dat, obj);
break;
case DWG_TYPE_SPLINE:
error = dwg_decode_SPLINE (dat, obj);
break;
case DWG_TYPE_REGION:
error = dwg_decode_REGION (dat, obj);
break;
case DWG_TYPE__3DSOLID:
error = dwg_decode__3DSOLID (dat, obj);
break;
case DWG_TYPE_BODY:
error = dwg_decode_BODY (dat, obj);
break;
case DWG_TYPE_RAY:
error = dwg_decode_RAY (dat, obj);
break;
case DWG_TYPE_XLINE:
error = dwg_decode_XLINE (dat, obj);
break;
case DWG_TYPE_DICTIONARY:
error = dwg_decode_DICTIONARY (dat, obj);
break;
case DWG_TYPE_MTEXT:
error = dwg_decode_MTEXT (dat, obj);
break;
case DWG_TYPE_LEADER:
error = dwg_decode_LEADER (dat, obj);
break;
case DWG_TYPE_TOLERANCE:
error = dwg_decode_TOLERANCE (dat, obj);
break;
case DWG_TYPE_MLINE:
error = dwg_decode_MLINE (dat, obj);
break;
case DWG_TYPE_BLOCK_CONTROL:
error = dwg_decode_BLOCK_CONTROL (dat, obj);
if (!error && obj->tio.object->tio.BLOCK_CONTROL)
{
if (!dwg->block_control.parent) // only once
dwg->block_control = *obj->tio.object->tio.BLOCK_CONTROL;
else
LOG_WARN ("Second BLOCK_CONTROL object ignored");
}
break;
case DWG_TYPE_BLOCK_HEADER:
error = dwg_decode_BLOCK_HEADER (dat, obj);
/*
* We cannot cache dwg->*space_block here as dwg->objects might get
* realloc'ed. See dwg_model_space_object() and dwg_paper_space_object()
* instead.
*/
break;
case DWG_TYPE_LAYER_CONTROL:
error = dwg_decode_LAYER_CONTROL (dat, obj);
break;
case DWG_TYPE_LAYER:
error = dwg_decode_LAYER (dat, obj);
break;
case DWG_TYPE_STYLE_CONTROL:
error = dwg_decode_STYLE_CONTROL (dat, obj);
break;
case DWG_TYPE_STYLE:
error = dwg_decode_STYLE (dat, obj);
break;
case DWG_TYPE_LTYPE_CONTROL:
error = dwg_decode_LTYPE_CONTROL (dat, obj);
break;
case DWG_TYPE_LTYPE:
error = dwg_decode_LTYPE (dat, obj);
break;
case DWG_TYPE_VIEW_CONTROL:
error = dwg_decode_VIEW_CONTROL (dat, obj);
break;
case DWG_TYPE_VIEW:
error = dwg_decode_VIEW (dat, obj);
break;
case DWG_TYPE_UCS_CONTROL:
error = dwg_decode_UCS_CONTROL (dat, obj);
break;
case DWG_TYPE_UCS:
error = dwg_decode_UCS (dat, obj);
break;
case DWG_TYPE_VPORT_CONTROL:
error = dwg_decode_VPORT_CONTROL (dat, obj);
break;
case DWG_TYPE_VPORT:
error = dwg_decode_VPORT (dat, obj);
break;
case DWG_TYPE_APPID_CONTROL:
error = dwg_decode_APPID_CONTROL (dat, obj);
break;
case DWG_TYPE_APPID:
error = dwg_decode_APPID (dat, obj);
break;
case DWG_TYPE_DIMSTYLE_CONTROL:
error = dwg_decode_DIMSTYLE_CONTROL (dat, obj);
break;
case DWG_TYPE_DIMSTYLE:
error = dwg_decode_DIMSTYLE (dat, obj);
break;
case DWG_TYPE_VX_CONTROL:
error = dwg_decode_VX_CONTROL (dat, obj);
break;
case DWG_TYPE_VX_TABLE_RECORD:
error = dwg_decode_VX_TABLE_RECORD (dat, obj);
break;
case DWG_TYPE_GROUP:
error = dwg_decode_GROUP (dat, obj);
break;
case DWG_TYPE_MLINESTYLE:
error = dwg_decode_MLINESTYLE (dat, obj);
break;
case DWG_TYPE_OLE2FRAME:
error = dwg_decode_OLE2FRAME (dat, obj);
break;
case DWG_TYPE_DUMMY:
error = dwg_decode_DUMMY (dat, obj);
break;
case DWG_TYPE_LONG_TRANSACTION:
error = dwg_decode_LONG_TRANSACTION (dat, obj);
break;
case DWG_TYPE_LWPOLYLINE:
error = dwg_decode_LWPOLYLINE (dat, obj);
break;
case DWG_TYPE_HATCH:
error = dwg_decode_HATCH (dat, obj);
break;
case DWG_TYPE_XRECORD:
error = dwg_decode_XRECORD (dat, obj);
break;
case DWG_TYPE_PLACEHOLDER:
error = dwg_decode_PLACEHOLDER (dat, obj);
break;
case DWG_TYPE_OLEFRAME:
error = dwg_decode_OLEFRAME (dat, obj);
break;
case DWG_TYPE_VBA_PROJECT:
// LOG_ERROR ("Unhandled Object VBA_PROJECT. Has its own section");
error = dwg_decode_VBA_PROJECT (dat, obj);
// error = DWG_ERR_UNHANDLEDCLASS;
break;
case DWG_TYPE_LAYOUT:
error = dwg_decode_LAYOUT (dat, obj);
break;
case DWG_TYPE_PROXY_ENTITY:
error = dwg_decode_PROXY_ENTITY (dat, obj);
break;
case DWG_TYPE_PROXY_OBJECT:
error = dwg_decode_PROXY_OBJECT (dat, obj);
break;
default:
if (obj->type == dwg->layout_type)
error = dwg_decode_LAYOUT (dat, obj);
/* > 500 */
else if ((error = dwg_decode_variable_type (dwg, dat, hdl_dat, obj))
& DWG_ERR_UNHANDLEDCLASS)
{
int is_entity = 0;
int i = obj->type - 500;
Dwg_Class *klass = NULL;
/* restart and read into the UNKNOWN_OBJ object */
/* the relative offset from type after common_entity_data */
// obj->common_size = bit_position(dat) - restartpos;
// LOG_HANDLE("common_size: %" PRIuSIZE "\n", obj->common_size); //
// needed for unknown
bit_set_position (dat, restartpos);
if (i >= 0 && i < (int)dwg->num_classes)
{
klass = &dwg->dwg_class[i];
is_entity = dwg_class_is_entity (klass);
}
else
{
if (i < 0)
LOG_ERROR ("Invalid class index %d < 0", i);
else
LOG_ERROR ("Invalid class index %d >= %d", i,
(int)dwg->num_classes);
obj->type = 0;
*dat = abs_dat;
return error | DWG_ERR_VALUEOUTOFBOUNDS;
}
// properly dwg_decode_object/_entity for eed, reactors, xdic
// Inventor's AcIdBlockReference is an AcDbBlockReference: read it as
// an INSERT (even when its class flags are unusable)
if (klass->dxfname
&& !strcmp (klass->dxfname, "ACIDBLOCKREFERENCE"))
{
obj->type = DWG_TYPE_INSERT;
error |= dwg_decode_INSERT (dat, obj);
}
else if (is_entity)
{
// obj->type = DWG_TYPE_UNKNOWN_ENT;
error |= dwg_decode_UNKNOWN_ENT (dat, obj);
}
else
{
// obj->type = DWG_TYPE_UNKNOWN_OBJ;
error |= dwg_decode_UNKNOWN_OBJ (dat, obj);
}
if (error >= DWG_ERR_CRITICAL)
*dat = abs_dat;
}
}
if (obj->handle.value)
{ // empty only with UNKNOWN
LOG_HANDLE (" object_map{" FORMAT_HV "} = %lu\n", obj->handle.value,
(unsigned long)num);
hash_set (dwg->object_map, obj->handle.value, (uint64_t)num);
}
if (dat->byte > 8 * dat->size)
{
LOG_ERROR ("Invalid object address (overflow): %" PRIuSIZE
" > %" PRIuSIZE,
dat->byte, 8 * dat->size);
*dat = abs_dat;
return error | DWG_ERR_INVALIDDWG;
}
/* Restore the old absolute chain.
CRC needs to be calculated from address, which is before our 0 position.
*/
restartpos = bit_position (dat);
*dat = abs_dat;
bit_set_position (dat, objpos + restartpos);
if (dat->byte >= dat->size)
{
LOG_ERROR ("Invalid object position (overflow): %" PRIuSIZE
" > %" PRIuSIZE,
dat->byte, dat->size);
*dat = abs_dat;
return error | DWG_ERR_INVALIDDWG;
}
/* Now 1 padding bits until next byte, and then a RS CRC */
if (dat->bit)
{
unsigned char r = 8 - dat->bit;
LOG_HANDLE (" padding: %X/%X (%d bits)\n", dat->chain[dat->byte],
dat->chain[dat->byte] & ((1 << r) - 1), r);
bit_advance_position (dat, r);
}
bit_set_position (dat, (obj->address + obj->size) * 8 - 2);
if (!bit_check_CRC (dat, address, 0xC0C1))
error |= DWG_ERR_WRONGCRC;
/* Reset to previous addresses for return */
*dat = abs_dat;
return realloced ? -1 : error; // re-alloced or not
}
/** dwg_decode_unknown
Container to hold a unknown class entity, see classes.inc
Every DEBUGGING class holds a bits array, a bitsize, and the handle
and string stream offsets.
It starts after the common_entity|object_data until and goes until the end
of final padding, to the CRC.
(obj->common_size/8 .. obj->size)
*/
int
dwg_decode_unknown_bits (Bit_Chain *restrict dat, Dwg_Object *restrict obj)
{
// bitsize does not include the handles size
int num_bytes;
size_t pos = bit_position (dat);
long num_bits = ((8 * obj->size) - pos) & ULONG_MAX;
if (num_bits < 0)
return DWG_ERR_VALUEOUTOFBOUNDS;
obj->num_unknown_bits = (BITCODE_RL)num_bits;
num_bytes = num_bits / 8;
if (num_bits % 8)
num_bytes++;
obj->unknown_bits = bit_read_bits (dat, num_bits);
if (!obj->unknown_bits)
{
bit_set_position (dat, pos);
obj->num_unknown_bits = 0;
return DWG_ERR_VALUEOUTOFBOUNDS;
}
// [num_bits (commonsize, hdlpos, strsize) num_bytes TF]
LOG_TRACE ("unknown_bits [%ld (%" PRIuSIZE ",%ld,%d) %d TF]: ", num_bits,
obj->common_size, (long)(obj->bitsize - obj->common_size),
(int)obj->stringstream_size, num_bytes);
LOG_TRACE_TF (obj->unknown_bits, num_bytes);
LOG_TRACE ("\n");
bit_set_position (dat, pos);
return 0;
}
int
dwg_decode_unknown_rest (Bit_Chain *restrict dat, Dwg_Object *restrict obj)
{
// check in which object stream we are: common, object, text or handles?
// for now we only need the text
// bitsize does not include the handles size
int num_bytes;
size_t pos = bit_position (dat);
long num_bits;
if (pos < obj->bitsize) // data or text
num_bits = (obj->bitsize - pos) & ULONG_MAX;
else // or handles
num_bits = ((8 * obj->size) - pos) & ULONG_MAX;
if (num_bits < 0)
return DWG_ERR_VALUEOUTOFBOUNDS;
obj->num_unknown_rest = (BITCODE_RL)num_bits;
num_bytes = num_bits / 8;
if (num_bits % 8)
num_bytes++;
obj->unknown_rest = bit_read_bits (dat, num_bits);
if (!obj->unknown_rest)
{
bit_set_position (dat, pos);
obj->num_unknown_rest = 0;
return DWG_ERR_VALUEOUTOFBOUNDS;
}
// [num_bits (commonsize, hdlpos, strsize) num_bytes TF]
LOG_TRACE ("unknown_rest [%ld (%" PRIuSIZE ",%ld,%d) %d TF]: ", num_bits,
obj->common_size, (long)(obj->bitsize - obj->common_size),
(int)obj->stringstream_size, num_bytes);
LOG_TRACE_TF (obj->unknown_rest, num_bytes);
LOG_TRACE ("\n");
bit_set_position (dat, pos);
return 0;
}
/* We need the full block name, not from BLOCK_HEADER, but the BLOCK entity.
unicode is allocated as utf-8.
*/
char *
dwg_dim_blockname (Dwg_Data *restrict dwg, const Dwg_Object *restrict obj)
{
BITCODE_H block = NULL;
if (dwg_dynapi_entity_value (obj->tio.entity->tio.DIMENSION_LINEAR,
obj->name, "block", &block, NULL))
{
return block ? dwg_handle_name (dwg, "BLOCK", block) : NULL;
}
return NULL;
}
/* Ensure that the given refs have rising objid's/indices (when read in natural
* order). With mips32 -O2 inline would fail.
*/
static int
ref_after (const Dwg_Object_Ref *restrict r1,
const Dwg_Object_Ref *restrict r2)
{
if (!r1 || !r2 || !r1->obj || !r2->obj)
return 0;
return r1->obj->index >= r2->obj->index ? 1 : 0;
}
/* just look at the next object, if it's a SEQEND (i.e ENDBLK) */
int
dwg_validate_INSERT (Dwg_Object *restrict obj)
{
Dwg_Object *restrict next = dwg_next_object (obj);
if (obj->fixedtype == DWG_TYPE_INSERT
|| obj->fixedtype == DWG_TYPE_ACIDBLOCKREFERENCE)
{
Dwg_Entity_INSERT *restrict _obj = obj->tio.entity->tio.INSERT;
Dwg_Object_Ref *restrict seqend = _obj->seqend;
if (!_obj->has_attribs)
return 1;
if (!seqend || next == seqend->obj)
{
LOG_TRACE ("unsorted INSERT " FORMAT_HV " SEQEND " FORMAT_RLLx
" ATTRIB\n",
obj->handle.value,
seqend && seqend->obj ? seqend->obj->handle.value : 0L);
return 0;
}
}
else if (obj->fixedtype == DWG_TYPE_MINSERT)
{
Dwg_Entity_MINSERT *restrict _obj = obj->tio.entity->tio.MINSERT;
Dwg_Object_Ref *restrict seqend = _obj->seqend;
if (!_obj->has_attribs)
return 1;
if (!seqend || next == seqend->obj)
{
LOG_TRACE ("unsorted INSERT " FORMAT_HV " SEQEND " FORMAT_RLLx
" ATTRIB\n",
obj->handle.value,
seqend && seqend->obj ? seqend->obj->handle.value : 0L);
return 0;
}
}
return 1;
}
int
dwg_validate_POLYLINE (Dwg_Object *restrict obj)
{
/* We ensured the common fields structure is shared with all 4 types */
Dwg_Entity_POLYLINE_2D *restrict _obj = obj->tio.entity->tio.POLYLINE_2D;
Dwg_Data *restrict dwg = obj->parent;
if (dwg->header.version > R_11)
{
Dwg_Object_Ref *restrict seqend = _obj->seqend;
/* if shifted in check_POLYLINE_handles() seqend might be empty */
if (!seqend)
{ /* either the first or last */
Dwg_Object *restrict next = dwg_next_object (obj);
if (next && next->fixedtype == DWG_TYPE_SEQEND)
{
seqend = dwg_find_objectref (
dwg, next); // usually not found, even with set hash
if (seqend == NULL)
{
seqend
= (Dwg_Object_Ref *)calloc (1, sizeof (Dwg_Object_Ref));
seqend->obj = next;
seqend->handleref = next->handle;
seqend->absolute_ref = next->handle.value;
dwg_decode_add_object_ref (dwg, seqend);
}
_obj->seqend = seqend;
LOG_WARN ("fixed empty POLYLINE.seqend with +1 obj");
}
else if (_obj->vertex)
{
Dwg_Object_Ref *restrict ref = _obj->vertex[_obj->num_owned - 1];
if (ref && ref->obj)
{
next = dwg_next_object (ref->obj);
if (next && next->fixedtype == DWG_TYPE_SEQEND)
{
seqend = dwg_find_objectref (dwg, next);
if (seqend == NULL)
{
seqend = (Dwg_Object_Ref *)calloc (
1, sizeof (Dwg_Object_Ref));
seqend->obj = next;
seqend->handleref = next->handle;
seqend->absolute_ref = next->handle.value;
dwg_decode_add_object_ref (dwg, seqend);
}
_obj->seqend = seqend;
LOG_WARN (
"fixed empty POLYLINE.seqend with last vertex +1");
}
}
}
}
}
if (dwg->header.version > R_11 && dwg->header.version <= R_2000)
{
Dwg_Object_Ref *restrict first_vertex = _obj->first_vertex;
Dwg_Object_Ref *restrict last_vertex = _obj->last_vertex;
Dwg_Object_Ref *restrict seqend = _obj->seqend;
if (ref_after (first_vertex, last_vertex)
|| ref_after (last_vertex, seqend))
{
LOG_TRACE ("unsorted POLYLINE VERTEX SEQEND\n");
return 0;
}
}
else if (dwg->header.version >= R_2004 && _obj->vertex)
{
BITCODE_BL i = 1;
Dwg_Object_Ref *restrict first_vertex = _obj->vertex[0];
Dwg_Object_Ref *restrict seqend = _obj->seqend;
if (ref_after (first_vertex, seqend))
{
/* r2010+ often mix up the hdlstream offset:
layer,vertex*,seqend. check the types then also */
if (first_vertex->obj->index < obj->index)
{
LOG_WARN ("skip wrong POLYLINE.vertex[0] handle " FORMAT_HV
" < " FORMAT_HV "\n",
first_vertex->obj->handle.value, obj->handle.value);
if (_obj->num_owned > 1)
first_vertex = _obj->vertex[1];
i = 2;
}
else
{
LOG_TRACE ("unsorted POLYLINE VERTEX SEQEND\n");
return 0;
}
}
for (; i < _obj->num_owned; i++)
{
if (ref_after (first_vertex, _obj->vertex[i])
|| ref_after (_obj->vertex[i], seqend))
{
LOG_TRACE ("unsorted POLYLINE VERTEX SEQEND\n");
return 0;
}
}
}
return 1;
}
/* Creates a handleref for an entity link (prev/next/first/last entity),
checking for cycles by traversing from first via next_entity.
Returns NULL if adding handle_val would create a cycle.
Used from importers (in_dxf.c, in_json.c) where ->obj pointers are valid.
Not used in the decode path itself, since refs are resolved post-decode. */
Dwg_Object_Ref *
dwg_add_entity_link (Dwg_Data *restrict dwg, const Dwg_Object *restrict first,
const char *restrict field, const BITCODE_RLL handle_val)
{
loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
if (!handle_val)
return dwg_add_handleref (dwg, 4, 0, NULL);
/* Check for cycles: traverse from first via next_entity */
if (first)
{
const Dwg_Object *obj = first;
BITCODE_BL steps = 0;
const BITCODE_BL max_steps = dwg->num_objects;
while (obj && steps++ < max_steps)
{
if (obj->handle.value == handle_val)
{
LOG_WARN ("Cycle in entity link %s: handle " FORMAT_RLLx
" already in chain\n",
field, handle_val);
return NULL;
}
if (obj->supertype != DWG_SUPERTYPE_ENTITY || !obj->tio.entity)
break;
{
const Dwg_Object_Ref *next = obj->tio.entity->next_entity;
if (!next || !next->absolute_ref || !next->obj)
break;
obj = next->obj;
}
}
}
return dwg_add_handleref (dwg, 4, handle_val, NULL);
}
/* Validate r13-r2000 entity link chains for cycles, using a visited bitset.
Called after resolve_objectref_vector in decode_R13_R2000.
Clears cyclic next_entity links when detected. */
static int
dwg_validate_entity_links (Dwg_Data *restrict dwg)
{
int changes = 0;
loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
if (dwg->header.version > R_2000 || dwg->header.version < R_13b1)
return 0;
LOG_TRACE ("\ndwg_validate_entity_links:\n");
for (BITCODE_BL i = 0; i < dwg->num_objects; i++)
{
Dwg_Object *hdr_obj = &dwg->object[i];
if (hdr_obj->fixedtype == DWG_TYPE_BLOCK_HEADER && hdr_obj->tio.object
&& hdr_obj->tio.object->tio.BLOCK_HEADER)
{
Dwg_Object_BLOCK_HEADER *_hdr
= hdr_obj->tio.object->tio.BLOCK_HEADER;
Dwg_Object *cur, *prev;
BITCODE_BL steps = 0;
const BITCODE_BL max_steps = dwg->num_objects;
uint8_t *visited;
size_t visited_size;
if (!_hdr || !_hdr->first_entity || !_hdr->first_entity->obj)
continue;
visited_size = ((size_t)max_steps + 7) / 8;
visited = calloc (visited_size, 1);
if (!visited)
continue;
prev = NULL;
cur = _hdr->first_entity->obj;
while (cur && steps < max_steps)
{
Dwg_Object_Entity *ent;
BITCODE_BL idx = cur->index;
if (idx >= max_steps)
{
if (prev && prev->tio.entity)
prev->tio.entity->next_entity = NULL;
changes++;
break;
}
if ((visited[idx / 8] >> (idx % 8)) & 1)
{
LOG_WARN (
"Cycle in entity links for BLOCK_HEADER " FORMAT_RLLx
": entity " FORMAT_RLLx " already visited, clearing\n",
hdr_obj->handle.value, cur->handle.value);
if (prev && prev->tio.entity)
prev->tio.entity->next_entity = NULL;
changes++;
break;
}
visited[idx / 8] |= (uint8_t)(1 << (idx % 8));
ent = cur->tio.entity;
if (cur->supertype != DWG_SUPERTYPE_ENTITY || !ent
|| !ent->next_entity || !ent->next_entity->obj)
break;
prev = cur;
cur = ent->next_entity->obj;
steps++;
}
free (visited);
}
}
LOG_TRACE ("\n");
return changes;
}
/* Set prev_ and next_entity handles from all block headers.
Needed after decode or import from r2004+ to r13-r2000. */
int
dwg_fixup_BLOCKS_entities (Dwg_Data *restrict dwg)
{
int changes = 0;
int is_uni = 0;
// also run for DXF imports (INDXF and ADD only, not INJSON), since
// in_postprocess_handles uses array index adjacency, not handle adjacency,
// to set nolinks. dwg_add_Document sets DWG_OPTS_IN = INDXF|INJSON for
// API-created documents; real DXF imports set only INDXF. Exclude the
// API-created case (INJSON set) to avoid clobbering first_entity before
// entities have been added.
if (dwg->header.version > R_2000
|| (dwg->header.from_version <= R_2000
&& (!(dwg->opts & DWG_OPTS_INDXF) || (dwg->opts & DWG_OPTS_INJSON))))
return 0;
is_uni = dwg->header.version >= R_2007;
loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
LOG_TRACE ("\ndwg_fixup_BLOCKS_entities:\n");
for (BITCODE_BL i = 0; i < dwg->num_objects; i++)
{
Dwg_Object *obj = &dwg->object[i];
if (obj->fixedtype == DWG_TYPE_BLOCK_HEADER)
{
Dwg_Object_BLOCK_HEADER *_obj = obj->tio.object->tio.BLOCK_HEADER;
char *_objname;
if (!_obj)
continue;
_objname
= is_uni ? bit_convert_TU ((BITCODE_TU)_obj->name) : _obj->name;
LOG_TRACE ("BLOCK_HEADER %s: %u\n", _objname,
(unsigned)_obj->num_owned);
if (!_obj->entities)
{
_obj->first_entity = dwg_add_handleref (dwg, 4, 0, NULL);
_obj->last_entity = dwg_add_handleref (dwg, 4, 0, NULL);
if (_obj->num_owned)
LOG_ERROR ("BLOCK_HEADER %s: %u => 0 num_owned\n", _objname,
(unsigned)_obj->num_owned);
_obj->num_owned = 0;
}
// link from first_entity to last_entity
for (BITCODE_BL j = 0; j < _obj->num_owned; j++)
{
Dwg_Object_Ref *hdl = _obj->entities[j];
Dwg_Object_Ref *prev = j > 0 ? _obj->entities[j - 1] : NULL;
Dwg_Object_Ref *next
= j + 1 < _obj->num_owned ? _obj->entities[j + 1] : NULL;
BITCODE_RLL prev_ref = prev ? prev->absolute_ref : 0;
BITCODE_RLL next_ref = next ? next->absolute_ref : 0;
BITCODE_RLL cur_ref = 0;
Dwg_Object *o = NULL;
Dwg_Object_Entity *ent = NULL;
if (!hdl)
continue;
cur_ref = hdl->absolute_ref;
LOG_HANDLE ("entities[%u]: " FORMAT_REF " \n", j,
ARGS_REF (hdl));
o = dwg_ref_object (dwg, hdl); // may fail!
ent = o ? o->tio.entity : NULL;
if (!o)
continue;
if (o->supertype != DWG_SUPERTYPE_ENTITY)
{
LOG_ERROR ("Illegal BLOCK_HEADER %s.entities[%u] %s",
_objname, j, o->name);
changes++;
if (is_uni)
free (_objname);
continue;
}
// only log changes
if (prev_ref == 0L && next_ref == 0L)
{
if (!ent->nolinks)
{
LOG_TRACE ("nolinks: 1\n");
ent->nolinks = 1;
changes++;
}
}
else if (prev_ref && prev_ref == cur_ref - 1 && next_ref
&& next_ref == cur_ref + 1)
{
if (!ent->nolinks)
{
LOG_TRACE ("nolinks: 1\n");
ent->nolinks = 1;
changes++;
}
}
else if (prev_ref && next_ref && ent->nolinks)
{
LOG_TRACE ("nolinks: 0\n");
ent->nolinks = 0;
changes++;
}
if (j == 0) // first: prev_entity must be NULL
{
if (!_obj->first_entity)
{
LOG_TRACE ("first_entity: " FORMAT_HV "\n",
hdl->absolute_ref);
_obj->first_entity
= dwg_add_handleref (dwg, 4, hdl->absolute_ref, o);
}
else if (_obj->first_entity->absolute_ref
!= hdl->absolute_ref)
{
LOG_WARN ("Fixup wrong BLOCK_HEADER %s.first_entity "
"from " FORMAT_HV " to " FORMAT_RLLx,
_objname, _obj->first_entity->absolute_ref,
hdl->absolute_ref);
changes++;
_obj->first_entity
= dwg_add_handleref (dwg, 4, hdl->absolute_ref, o);
}
}
if (ent->prev_entity == NULL)
{
if (!prev_ref)
{
LOG_TRACE ("nolinks: 0\n");
ent->nolinks = 0;
}
LOG_TRACE (" " FORMAT_HV ": prev_entity " FORMAT_RLLx ", ",
hdl->absolute_ref, prev_ref);
ent->prev_entity = dwg_add_handleref (dwg, 4, prev_ref, o);
}
else if (ent->prev_entity->absolute_ref != prev_ref)
{
LOG_WARN ("Fixup wrong BLOCK_HEADER "
"%s.entities[%d].prev_entity from " FORMAT_HV
" to " FORMAT_HV,
_objname, j, ent->prev_entity->absolute_ref,
prev_ref);
changes++;
ent->prev_entity = dwg_add_handleref (dwg, 4, prev_ref, o);
}
if (ent->next_entity == NULL)
{
LOG_TRACE (" next_entity " FORMAT_HV "\n", next_ref);
ent->next_entity = dwg_add_handleref (dwg, 4, next_ref, o);
if (!next_ref)
{
LOG_TRACE (" nolinks: 0\n");
ent->nolinks = 0;
}
}
else if (ent->next_entity->absolute_ref != next_ref)
{
LOG_WARN ("Fixup wrong BLOCK_HEADER "
"%s.entities[%d].next_entity from " FORMAT_HV
" to " FORMAT_HV,
_objname, j, ent->next_entity->absolute_ref,
next_ref);
changes++;
ent->next_entity = dwg_add_handleref (dwg, 4, next_ref, o);
}
if (j == _obj->num_owned - 1) // last: next_entity must be NULL
{
if (!_obj->last_entity)
{
LOG_TRACE ("last_entity: " FORMAT_HV "\n",
hdl->absolute_ref);
_obj->last_entity
= dwg_add_handleref (dwg, 4, hdl->absolute_ref, o);
}
else if (_obj->last_entity->absolute_ref
!= hdl->absolute_ref)
{
LOG_WARN ("Fixup wrong BLOCK_HEADER %s.last_entity "
"from " FORMAT_HV " to " FORMAT_RLLx,
_objname, _obj->last_entity->absolute_ref,
hdl->absolute_ref);
changes++;
_obj->last_entity
= dwg_add_handleref (dwg, 4, hdl->absolute_ref, o);
}
}
}
if (is_uni)
free (_objname);
}
}
// DXF roundtrip may leave VERTEX/SEQEND children of block polylines
// with entmode=2 (model space). Fix them up by scanning backwards
// for the POLYLINE/INSERT owner and inheriting its entmode.
for (BITCODE_BL i = 0; i < dwg->num_objects; i++)
{
Dwg_Object *obj = &dwg->object[i];
if (obj->supertype != DWG_SUPERTYPE_ENTITY || !obj->tio.entity)
continue;
if (obj->tio.entity->entmode != 2)
continue;
if (obj->fixedtype != DWG_TYPE_VERTEX_2D
&& obj->fixedtype != DWG_TYPE_VERTEX_3D
&& obj->fixedtype != DWG_TYPE_VERTEX_MESH
&& obj->fixedtype != DWG_TYPE_VERTEX_PFACE
&& obj->fixedtype != DWG_TYPE_VERTEX_PFACE_FACE
&& obj->fixedtype != DWG_TYPE_SEQEND)
continue;
// Scan backwards for the POLYLINE/INSERT owner
for (BITCODE_BL j = i; j > 0; j--)
{
Dwg_Object *owner = &dwg->object[j - 1];
if (owner->fixedtype == DWG_TYPE_INSERT
|| owner->fixedtype == DWG_TYPE_ACIDBLOCKREFERENCE
|| owner->fixedtype == DWG_TYPE_MINSERT
|| owner->fixedtype == DWG_TYPE_POLYLINE_2D
|| owner->fixedtype == DWG_TYPE_POLYLINE_3D
|| owner->fixedtype == DWG_TYPE_POLYLINE_PFACE
|| owner->fixedtype == DWG_TYPE_POLYLINE_MESH)
{
if (owner->tio.entity->entmode == 3)
{
obj->tio.entity->entmode = 3;
LOG_TRACE ("Fixup %s.entmode -> 3 (owner %s)\n", obj->name,
owner->name);
changes++;
}
break;
}
}
}
dwg_validate_entity_links (dwg);
LOG_TRACE ("\n");
return changes;
}
// for DXF
// length 38
void
dxf_3dsolid_revisionguid (Dwg_Entity_3DSOLID *_obj)
{
sprintf ((char *)_obj->revision_guid,
"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
(unsigned long)_obj->revision_major,
(unsigned)_obj->revision_minor1, (unsigned)_obj->revision_minor2,
_obj->revision_bytes[0], _obj->revision_bytes[1],
_obj->revision_bytes[2], _obj->revision_bytes[3],
_obj->revision_bytes[4], _obj->revision_bytes[5],
_obj->revision_bytes[6], _obj->revision_bytes[7]);
LOG_TRACE ("revision_guid: %s\n", (char *)_obj->revision_guid);
}
int
decode_preR13_DIMENSION (Bit_Chain *restrict dat, Dwg_Object *restrict obj)
{
int error = dwg_setup_DIMENSION_ANG2LN (obj);
Dwg_Object_Entity *_ent = obj->tio.entity;
Bit_Chain *hdl_dat = dat;
Dwg_Data *dwg = obj->parent;
BITCODE_RC dimtype = 0;
{
// decode a generic DIMENSION, and fixup the type after. DIMENSION_ANG2LN
// is the biggest.
Dwg_Entity_DIMENSION_ANG2LN *_obj;
Bit_Chain *str_dat = dat;
LOG_INFO ("Decode entity DIMENSION\n");
_obj = _ent->tio.DIMENSION_ANG2LN;
error |= decode_entity_preR13 (dat, obj, _ent);
COMMON_ENTITY_DIMENSION
dimtype = _obj->flag;
}
switch (dimtype & 15)
{
case FLAG_R11_DIMENSION_LINEAR:
{
Dwg_Entity_DIMENSION_LINEAR *_obj = _ent->tio.DIMENSION_LINEAR;
obj->fixedtype = DWG_TYPE_DIMENSION_LINEAR;
obj->name = obj->dxfname = (char *)"DIMENSION_LINEAR";
LOG_TRACE ("=> Entity DIMENSION_LINEAR\n");
if (R11OPTS (8))
{
if (dat->version >= R_10)
FIELD_3RD (xline1_pt, 13)
else
FIELD_2RD (xline1_pt, 13)
}
if (R11OPTS (16))
{
if (dat->version >= R_10)
FIELD_3RD (xline2_pt, 14)
else
FIELD_2RD (xline2_pt, 14)
}
if (R11OPTS (0x100))
FIELD_RD0 (dim_rotation, 50);
if (R11OPTS (0x200))
FIELD_RD0 (oblique_angle, 52); // ext_line_rotation
if (R11OPTS (0x400))
FIELD_RD0 (text_rotation, 53);
if (R11OPTS (0x4000))
FIELD_3RD (extrusion, 210);
if (R11OPTS (0x8000))
FIELD_HANDLE (dimstyle, 2, 0);
}
break;
case FLAG_R11_DIMENSION_ALIGNED:
{
Dwg_Entity_DIMENSION_ALIGNED *_obj = _ent->tio.DIMENSION_ALIGNED;
obj->fixedtype = DWG_TYPE_DIMENSION_ALIGNED;
obj->name = obj->dxfname = (char *)"DIMENSION_ALIGNED";
LOG_TRACE ("=> Entity DIMENSION_ALIGNED\n");
if (R11OPTS (8))
{
if (dat->version >= R_10)
FIELD_3RD (xline1_pt, 13)
else
FIELD_2RD (xline1_pt, 13)
}
if (R11OPTS (16))
{
if (dat->version >= R_10)
FIELD_3RD (xline2_pt, 14)
else
FIELD_2RD (xline2_pt, 14)
}
if (R11OPTS (0x100))
FIELD_RD (oblique_angle, 50);
if (R11OPTS (0x400))
FIELD_RD0 (text_rotation, 53);
if (R11OPTS (0x8000))
FIELD_HANDLE (dimstyle, 2, 0);
}
break;
case FLAG_R11_DIMENSION_ANG2LN:
{
Dwg_Entity_DIMENSION_ANG2LN *_obj = _ent->tio.DIMENSION_ANG2LN;
obj->fixedtype = DWG_TYPE_DIMENSION_ANG2LN;
obj->name = obj->dxfname = (char *)"DIMENSION_ANG2LN";
if (R11OPTS (8))
{
if (dat->version >= R_10)
FIELD_3RD (xline1start_pt, 13)
else
FIELD_2RD (xline1start_pt, 13)
}
if (R11OPTS (16))
{
if (dat->version >= R_10)
FIELD_3RD (xline1end_pt, 14)
else
FIELD_2RD (xline1end_pt, 14)
}
if (R11OPTS (32))
{
if (dat->version >= R_10)
FIELD_3RD (xline2start_pt, 15)
else
FIELD_2RD (xline2start_pt, 15)
}
if (R11OPTS (64))
FIELD_2RD (xline2end_pt, 16);
if (R11OPTS (0x400))
FIELD_RD0 (text_rotation, 53);
if (R11OPTS (0x8000))
FIELD_HANDLE (dimstyle, 2, 0);
}
break;
case FLAG_R11_DIMENSION_DIAMETER:
{
Dwg_Entity_DIMENSION_DIAMETER *_obj = _ent->tio.DIMENSION_DIAMETER;
obj->fixedtype = DWG_TYPE_DIMENSION_DIAMETER;
obj->name = obj->dxfname = (char *)"DIMENSION_DIAMETER";
LOG_TRACE ("=> Entity DIMENSION_DIAMETER\n");
if (R11OPTS (32))
{
if (dat->version >= R_10 && !R11FLAG (FLAG_R11_HAS_ELEVATION))
FIELD_3RD (first_arc_pt, 15)
else
FIELD_2RD (first_arc_pt, 15)
}
if (R11OPTS (128))
FIELD_RD (leader_len, 40);
if (R11OPTS (0x400))
FIELD_RD0 (text_rotation, 53);
if (R11OPTS (0x4000))
FIELD_3RD (extrusion, 210);
if (R11OPTS (0x8000))
FIELD_HANDLE (dimstyle, 2, 0);
}
break;
case FLAG_R11_DIMENSION_RADIUS:
{
Dwg_Entity_DIMENSION_RADIUS *_obj = _ent->tio.DIMENSION_RADIUS;
obj->fixedtype = DWG_TYPE_DIMENSION_RADIUS;
obj->name = obj->dxfname = (char *)"DIMENSION_RADIUS";
LOG_TRACE ("=> Entity DIMENSION_RADIUS\n");
if (R11OPTS (32))
{
if (dat->version >= R_10)
FIELD_3RD (first_arc_pt, 15)
else
FIELD_2RD (first_arc_pt, 15)
}
if (R11OPTS (128))
FIELD_RD (leader_len, 40);
if (R11OPTS (0x400))
FIELD_RD0 (text_rotation, 53);
if (R11OPTS (0x4000))
FIELD_3RD (extrusion, 210);
if (R11OPTS (0x8000))
FIELD_HANDLE (dimstyle, 2, 0);
}
break;
case FLAG_R11_DIMENSION_ANG3PT:
{
Dwg_Entity_DIMENSION_ANG3PT *_obj = _ent->tio.DIMENSION_ANG3PT;
obj->fixedtype = DWG_TYPE_DIMENSION_ANG3PT;
obj->name = obj->dxfname = (char *)"DIMENSION_ANG3PT";
LOG_TRACE ("=> Entity DIMENSION_ANG3PT\n");
if (R11OPTS (8))
{
if (dat->version >= R_10)
FIELD_3RD (xline1_pt, 13)
else
FIELD_2RD (xline1_pt, 13)
}
if (R11OPTS (16))
{
if (dat->version >= R_10)
FIELD_3RD (xline2_pt, 14)
else
FIELD_2RD (xline2_pt, 14)
}
if (R11OPTS (32))
{
if (dat->version >= R_10)
FIELD_3RD (center_pt, 15)
else
FIELD_2RD (center_pt, 15)
}
if (R11OPTS (64))
FIELD_2RD (xline2end_pt, 0)
if (R11OPTS (0x400))
FIELD_RD0 (text_rotation, 53);
if (R11OPTS (0x8000))
FIELD_HANDLE (dimstyle, 2, 0);
}
break;
case FLAG_R11_DIMENSION_ORDINATE:
{
Dwg_Entity_DIMENSION_ORDINATE *_obj = _ent->tio.DIMENSION_ORDINATE;
obj->fixedtype = DWG_TYPE_DIMENSION_ORDINATE;
obj->name = obj->dxfname = (char *)"DIMENSION_ORDINATE";
LOG_TRACE ("=> Entity DIMENSION_ORDINATE\n");
if (R11OPTS (8))
{ // if dxf 13 (extension_defining_pt)
if (dat->version >= R_10)
FIELD_3RD (feature_location_pt, 13)
else
FIELD_2RD (feature_location_pt, 13)
}
if (R11OPTS (16))
{ // extension_defining_point2
if (dat->version >= R_10)
FIELD_3RD (leader_endpt, 14)
else
FIELD_2RD (leader_endpt, 14)
}
if (R11OPTS (0x400))
FIELD_RD0 (text_rotation, 53);
if (R11OPTS (0x8000))
FIELD_HANDLE (dimstyle, 2, 0);
}
break;
default:
LOG_ERROR ("Unknown preR13 DIMENSION type %u", dimtype);
error |= DWG_ERR_VALUEOUTOFBOUNDS;
}
return error;
}
int
decode_preR13_sentinel (const Dwg_Sentinel sentinel,
const char *restrict sentinel_name,
Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
int error = 0;
const unsigned char *const wanted = dwg_sentinel (sentinel);
BITCODE_TF r11_sentinel;
if (dat->byte + 16 > dat->size)
{
LOG_ERROR ("%s buffer overflow at pos %" PRIuSIZE " > size %" PRIuSIZE,
__FUNCTION__, dat->byte + 16, dat->size);
return DWG_ERR_INVALIDDWG;
}
r11_sentinel = bit_read_TF (dat, 16U);
if (!r11_sentinel)
return DWG_ERR_INVALIDDWG;
LOG_TRACE ("%s: ", sentinel_name);
LOG_RPOS;
LOG_TRACE_TF (r11_sentinel, 16);
if (memcmp (r11_sentinel, wanted, 16))
{
/* Search +- 1000 bytes around, as the comment always said. It was
actually +-200 (pos = byte - 200, len = 400), which misses real files:
a r11 drawing here has every table sentinel displaced from where the
header's table address puts it, LAYER_BEGIN by -300 and VIEW_BEGIN by
-608, so this recovery never fired and the whole file was rejected. */
const size_t window = 1000;
size_t pos = dat->byte > window ? dat->byte - window : 0;
size_t len = MIN (dat->size - pos, 2 * window);
char *found = (char *)memmem (&dat->chain[pos], len, wanted, 16);
if (!found)
{
// The sentinel is a delimiter, not the locator: records are read
// from tbl->address (which the header states independently) and
// each carries its own CRC. A missing sentinel (e.g. on an empty
// table) is the writer being irregular, not a reason to reject the
// file.
LOG_WARN ("%s not found at %" PRIuSIZE ", reading on", sentinel_name,
dat->byte - 16);
}
else
{
pos = (ptrdiff_t)found - (ptrdiff_t)&dat->chain[0];
LOG_WARN ("%s not found at %" PRIuSIZE ", but at %" PRIuSIZE,
sentinel_name, dat->byte - 16, pos);
dat->byte = pos + 16;
error = DWG_ERR_WRONGCRC;
}
}
free (r11_sentinel);
return error;
}
static void
decode_preR13_section_chk (Dwg_Section_Type_r11 id, Bit_Chain *restrict dat,
Dwg_Data *restrict dwg)
{
Dwg_Section *tbl = &dwg->header.section[id];
BITCODE_RS id1, size;
BITCODE_RLL address;
BITCODE_RLd number;
#define CMP(f, type) \
if (tbl->f != f) \
LOG_ERROR ("decode_preR13_section_chk %s %s", tbl->name, #f);
if ((BITCODE_RL)id > dwg->header.num_sections)
{
LOG_ERROR (
"decode_preR13_section_chk: Invalid table %u, have only " FORMAT_RL,
(unsigned)id, dwg->header.num_sections);
return;
}
id1 = bit_read_RS (dat);
size = bit_read_RS (dat);
CMP (size, RS);
number = (BITCODE_RLd)bit_read_RS (dat);
CMP (number, RLd);
address = (BITCODE_RLL)bit_read_RL (dat);
CMP (address, RLL)
#undef CMP
LOG_TRACE ("chk table %-8s [%2d]: size:%-4u nr:%-3ld (" FORMAT_RLL ")\n",
tbl->name, (int)id1, (unsigned)size, (long)number, address);
}
#define DECODE_PRER13_SENTINEL(ID) \
error |= decode_preR13_sentinel (ID, #ID, dat, dwg)
// only in R11
int
decode_r11_auxheader (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
int error = 0;
BITCODE_RS crc, crcc;
Dwg_AuxHeader *_obj = &dwg->auxheader;
size_t pos = dat->byte;
LOG_TRACE ("\nAUXHEADER: @0x%zx\n", dat->byte);
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_AUXHEADER_BEGIN);
FIELD_RS (num_auxheader_variables, 0);
FIELD_RS (auxheader_size, 0);
FIELD_RLx (entities_start, 0);
if (_obj->entities_start != dwg->header.entities_start)
{
LOG_WARN ("entities_start %x/%x", _obj->entities_start,
dwg->header.entities_start);
}
FIELD_RLx (entities_end, 0);
if (_obj->entities_end != dwg->header.entities_end)
{
LOG_WARN ("entities_end %x/%x", _obj->entities_end,
dwg->header.entities_end);
}
FIELD_RLx (blocks_start, 0);
if (_obj->blocks_start != dwg->header.blocks_start)
{
LOG_WARN ("blocks_start %x/%x", _obj->blocks_start,
dwg->header.blocks_start);
}
FIELD_RLx (extras_start, 0);
if (_obj->extras_start != dwg->header.extras_start)
{
LOG_WARN ("extras_start %x/%x", _obj->extras_start,
dwg->header.extras_start);
}
FIELD_RS (R11_HANDLING, 0);
_obj->HANDSEED = bit_read_RLL_BE (dat);
LOG_TRACE ("HANDSEED: " FORMAT_RLLx " [RLLx 0]\n", _obj->HANDSEED);
FIELD_RS (num_aux_tables, 0);
decode_preR13_section_chk (SECTION_BLOCK, dat, dwg);
decode_preR13_section_chk (SECTION_LAYER, dat, dwg);
decode_preR13_section_chk (SECTION_STYLE, dat, dwg);
decode_preR13_section_chk (SECTION_LTYPE, dat, dwg);
decode_preR13_section_chk (SECTION_VIEW, dat, dwg);
if (dwg->header.num_sections >= SECTION_VPORT)
{
decode_preR13_section_chk (SECTION_UCS, dat, dwg);
decode_preR13_section_chk (SECTION_VPORT, dat, dwg);
}
if (dwg->header.num_sections >= SECTION_APPID)
{
decode_preR13_section_chk (SECTION_APPID, dat, dwg);
}
if (dwg->header.num_sections >= SECTION_VX)
{
decode_preR13_section_chk (SECTION_DIMSTYLE, dat, dwg);
decode_preR13_section_chk (SECTION_VX, dat, dwg);
}
FIELD_RLx (auxheader_address, 0);
if (_obj->auxheader_address < pos
// lower bound
|| _obj->auxheader_address > UINT32_MAX - 16
// upper bound
|| _obj->auxheader_address + 16 + _obj->auxheader_size > dat->size)
{
LOG_ERROR ("Invalid auxheader_address %04X or size %u",
_obj->auxheader_address, (unsigned)_obj->auxheader_size);
error |= DWG_ERR_WRONGCRC;
return error;
}
if (_obj->auxheader_address != pos)
{
LOG_WARN ("Invalid auxheader_address %04X", _obj->auxheader_address);
error |= DWG_ERR_WRONGCRC;
}
else if (_obj->auxheader_size < 2)
{
LOG_WARN ("Invalid auxheader_size " FORMAT_RS, _obj->auxheader_size);
error |= DWG_ERR_WRONGCRC;
}
else
{
crcc = bit_calc_CRC (0xC0C1,
// after sentinel (16 bytes)
&dat->chain[_obj->auxheader_address + 16],
// minus crc length (2 bytes)
_obj->auxheader_size - 2);
crc = bit_read_RS (dat);
LOG_TRACE ("crc: %04X [RSx] from 0x%x-0x%zx\n", crc,
_obj->auxheader_address + 16, dat->byte - 2);
if (crc != crcc)
{
LOG_ERROR ("AUX header CRC mismatch %04X <=> %04X", crc, crcc);
error |= DWG_ERR_WRONGCRC;
}
}
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_AUXHEADER_END);
LOG_TRACE ("\n");
return error;
}
// sentinel on begin and end is part of this decoding in case of R11
// start and end addresses are without sentinel
int
decode_preR13_entities (BITCODE_RL start, BITCODE_RL end,
unsigned num_entities, BITCODE_RL size,
Bit_Chain *restrict dat, Dwg_Data *restrict dwg,
const EntitySectionIndexR11 entity_section)
{
int error = 0;
BITCODE_BL num = dwg->num_objects;
BITCODE_RL real_start = start;
size_t oldpos;
BITCODE_RLL hdr_handle = 0;
const char *entities_section[]
= { "entities", "blocks entities", "extras entities" };
Dwg_Object *hdr = NULL;
Dwg_Object_BLOCK_HEADER *_hdr = NULL;
BITCODE_BL block_idx = 0, hdr_index = 0;
BITCODE_BL ps_hdr_index = 0;
Dwg_Object_BLOCK_HEADER *ps_hdr = NULL;
BITCODE_RLL ps_hdr_handle = 0;
LOG_TRACE ("\n%s: (" FORMAT_RLx "-" FORMAT_RLx " (%u), size " FORMAT_RL
")\n",
entities_section[entity_section], start, end, num_entities, size);
LOG_INFO ("==========================================\n");
// we only have a 2nd entities_end, not for blocks nor extras
if (end > dat->size && entity_section == ENTITIES_SECTION_INDEX
&& dwg->auxheader.entities_end && end != dwg->auxheader.entities_end)
{
LOG_ERROR ("Corrupt entities_end, fixed to "
"auxheader.entities_end " FORMAT_RL,
dwg->auxheader.entities_end);
end = dwg->header.entities_end = dwg->auxheader.entities_end;
}
if (end > dat->size)
{
LOG_ERROR ("Corrupt end " FORMAT_RL " fixed to filesize", end);
end = dat->size & 0xFFFFFFFF;
if (start < end)
size = end - start;
else
size = 0;
}
if (entity_section != BLOCKS_SECTION_INDEX)
{
hdr = dwg_model_space_object (dwg);
if (hdr && hdr->fixedtype == DWG_TYPE_BLOCK_HEADER)
{
hdr_index = hdr->index;
_hdr = hdr->tio.object->tio.BLOCK_HEADER;
_hdr->block_offset_r11 = (BITCODE_RL)-1;
if (!hdr->handle.value)
hdr->handle.value = dwg_next_handle (dwg);
hdr_handle = hdr->handle.value;
LOG_TRACE ("owned by BLOCK %s (" FORMAT_HV ")\n", _hdr->name,
hdr_handle);
}
if (entity_section != BLOCKS_SECTION_INDEX)
{
// pre-R13: paper space entities (entmode 1) are owned by the
// *PAPER_SPACE header, created by dwg_add_Document() since R_11
Dwg_Object *ps = dwg_paper_space_object (dwg);
if (ps && ps->fixedtype == DWG_TYPE_BLOCK_HEADER)
{
if (!ps->handle.value)
ps->handle.value = dwg_next_handle (dwg);
ps_hdr_index = ps->index;
ps_hdr = ps->tio.object->tio.BLOCK_HEADER;
ps_hdr_handle = ps->handle.value;
}
}
}
// TODO search current offset in block_offset_r11 in BLOCK_HEADER's
// with sentinel in case of R11
SINCE (R_11)
{
real_start -= 16;
}
// report unknown data before entities block
if (start != end && real_start > 0 && (BITCODE_RL)dat->byte != real_start)
{
LOG_WARN ("\n@0x%zx => start 0x%x", dat->byte, real_start);
if ((BITCODE_RL)dat->byte < real_start)
{
if (real_start > dat->size)
{
UNKNOWN_UNTIL (dat->size);
}
else
{
UNKNOWN_UNTIL (real_start);
}
}
}
SINCE (R_11)
{
switch (entity_section)
{
case ENTITIES_SECTION_INDEX:
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_ENTITIES_BEGIN);
break;
case BLOCKS_SECTION_INDEX:
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_BLOCK_ENTITIES_BEGIN);
break;
case EXTRAS_SECTION_INDEX:
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_EXTRA_ENTITIES_BEGIN);
break;
default:
LOG_ERROR ("Internal error: Illegal entity_section %d 0-2\n",
(int)entity_section);
return DWG_ERR_INTERNALERROR;
}
}
if (end > start && start == dat->byte)
{
oldpos = dat->byte;
dat->bit = 0;
while (dat->byte < oldpos + size)
{
size_t iter_start = dat->byte;
Dwg_Object *obj;
Dwg_Object_Type_r11 abstype;
BITCODE_RC pline_flag;
if (!num)
dwg->object
= (Dwg_Object *)calloc (REFS_PER_REALLOC, sizeof (Dwg_Object));
else if (num >= dwg->num_alloced_objects)
{
if (num >= 10000000)
{
LOG_ERROR ("Too many objects, abort");
return DWG_ERR_VALUEOUTOFBOUNDS;
}
while (num >= dwg->num_alloced_objects)
dwg->num_alloced_objects *= 2;
dwg->object = (Dwg_Object *)realloc (
dwg->object, dwg->num_alloced_objects * sizeof (Dwg_Object));
LOG_TRACE ("REALLOC dwg->object vector to %u\n",
dwg->num_alloced_objects);
dwg->dirty_refs = 1;
}
if (!dwg->object)
{
LOG_ERROR ("Out of memory");
return DWG_ERR_OUTOFMEM;
}
obj = &dwg->object[num];
memset (obj, 0, sizeof (Dwg_Object));
dwg->num_objects++;
obj->index = num;
obj->parent = dwg;
obj->address = dat->byte;
if (entity_section == BLOCKS_SECTION_INDEX)
obj->address |= 0x40000000; // to set entmode to 3
obj->supertype = DWG_SUPERTYPE_ENTITY;
LOG_HANDLE ("@offset 0x%zx\n", dat->byte - start);
PRE (R_2_0b)
{
obj->type = bit_read_RS (dat);
LOG_TRACE ("type: " FORMAT_RS " [RS]\n", obj->type);
if (obj->type > 127)
{ // deleted. moved into BLOCK
abstype = (Dwg_Object_Type_r11)abs ((int8_t)obj->type);
LOG_TRACE ("deleted\n");
}
else
{
abstype = (Dwg_Object_Type_r11)obj->type;
}
}
else
{
obj->type = bit_read_RC (dat);
LOG_TRACE ("type: " FORMAT_RCd " [RCd]\n", obj->type);
if (obj->type > 127)
{ // deleted. moved into BLOCK
abstype = (Dwg_Object_Type_r11)((unsigned)obj->type & 0x7F);
LOG_TRACE ("deleted\n");
}
else
{
abstype = (Dwg_Object_Type_r11)obj->type;
}
}
switch (abstype)
{
case DWG_TYPE_LINE_r11:
error |= dwg_decode_LINE (dat, obj);
break;
case DWG_TYPE_POINT_r11:
error |= dwg_decode_POINT (dat, obj);
break;
case DWG_TYPE_CIRCLE_r11:
error |= dwg_decode_CIRCLE (dat, obj);
break;
case DWG_TYPE_SHAPE_r11:
error |= dwg_decode_SHAPE (dat, obj);
break;
case DWG_TYPE_REPEAT_r11:
error |= dwg_decode_REPEAT (dat, obj);
break;
case DWG_TYPE_ENDREP_r11:
error |= dwg_decode_ENDREP (dat, obj);
break;
case DWG_TYPE_TEXT_r11:
error |= dwg_decode_TEXT (dat, obj);
break;
case DWG_TYPE_ARC_r11:
error |= dwg_decode_ARC (dat, obj);
break;
case DWG_TYPE_TRACE_r11:
error |= dwg_decode_TRACE (dat, obj);
break;
case DWG_TYPE_LOAD_r11:
error |= dwg_decode_LOAD (dat, obj);
break;
case DWG_TYPE_SOLID_r11:
error |= dwg_decode_SOLID (dat, obj);
break;
case DWG_TYPE_BLOCK_r11:
{
BITCODE_RL cur_offset;
BITCODE_RL cur_offset_prefix = 0;
if (dat->version > R_2_22)
cur_offset_prefix += 0x40000000;
// search current offset in block_offset_r11 in BLOCK_HEADER's
// and set new _hdr
cur_offset = (((dat->byte - 1) - start) | cur_offset_prefix)
& 0xFFFFFFFF;
error |= dwg_decode_BLOCK (dat, obj);
if (!_hdr && entity_section == BLOCKS_SECTION_INDEX)
{
for (BITCODE_BL i = 0; i < dwg->num_objects; i++)
{
Dwg_Object *o = &dwg->object[i];
if (o->fixedtype == DWG_TYPE_BLOCK_HEADER
&& o->tio.object && o->tio.object->tio.BLOCK_HEADER
&& cur_offset
== o->tio.object->tio.BLOCK_HEADER
->block_offset_r11)
{
LOG_TRACE ("found BLOCK_HEADER \"%s\" at "
"block_offset_r11 "
"0x%x\n",
o->tio.object->tio.BLOCK_HEADER->name,
cur_offset);
hdr = o;
hdr_handle = hdr->handle.value;
hdr_index = o->index;
_hdr = o->tio.object->tio.BLOCK_HEADER;
if (!obj->handle.value)
obj->handle.value = dwg_next_handle (dwg);
if (!_hdr->block_entity)
_hdr->block_entity = dwg_add_handleref (
dwg, 3, obj->handle.value, obj);
else
{
_hdr->block_entity->handleref.code = 3;
_hdr->block_entity->absolute_ref
= _hdr->block_entity->handleref.value
= obj->handle.value;
_hdr->block_entity->obj = obj;
}
LOG_TRACE (
"BLOCK_HEADER.block_entity: " FORMAT_HREF11
"\n",
ARGS_HREF11 (_hdr->block_entity));
if (!obj->tio.entity->tio.BLOCK->name)
obj->tio.entity->tio.BLOCK->name
= strdup (_hdr->name);
// LOG_TRACE ("next entities owned by BLOCK \"%s\"
// (%lx)\n",
// _hdr->name, hdr_handle);
block_idx++;
break;
}
}
if (!_hdr)
{
const char *name
= obj->fixedtype == DWG_TYPE_BLOCK
? obj->tio.entity->tio.BLOCK->name
: "(null)";
LOG_WARN (
"found no BLOCK_HEADER %s block_offset_r11 0x%x\n",
name, cur_offset);
hdr = NULL;
}
}
}
break;
case DWG_TYPE_ENDBLK_r11:
error |= dwg_decode_ENDBLK (dat, obj);
if (_hdr)
{
hdr = &dwg->object[hdr_index];
_hdr->endblk_entity
= dwg_add_handleref (dwg, 3, obj->handle.value, hdr);
LOG_TRACE ("BLOCK_HEADER.endblk_entity: " FORMAT_HREF11 "\n",
ARGS_HREF11 (_hdr->endblk_entity));
}
hdr = NULL;
_hdr = NULL;
break;
case DWG_TYPE_INSERT_r11:
error |= dwg_decode_INSERT (dat, obj);
break;
case DWG_TYPE_ATTDEF_r11:
error |= dwg_decode_ATTDEF (dat, obj);
break;
case DWG_TYPE_ATTRIB_r11:
error |= dwg_decode_ATTRIB (dat, obj);
break;
case DWG_TYPE_SEQEND_r11:
error |= dwg_decode_SEQEND (dat, obj);
break;
case DWG_TYPE_JUMP_r11:
error |= dwg_decode_JUMP (dat, obj);
break;
case DWG_TYPE_POLYLINE_r11:
{ // which polyline
BITCODE_RC flag_r11;
BITCODE_RS opts_r11;
BITCODE_RC extra_r11 = 0;
BITCODE_RS eed_size;
BITCODE_RC handling_len;
size_t start_byte;
LOG_TRACE ("Detect polyline:");
start_byte = dat->byte;
LOG_TRACE (" start_byte: %" PRIuSIZE ",", start_byte);
flag_r11 = bit_read_RC (dat);
LOG_TRACE (" flag_r11: 0x%x,", flag_r11);
dat->byte += 4;
opts_r11 = bit_read_RS (dat);
LOG_TRACE (" opts_r11: 0x%x", opts_r11);
if (opts_r11 & OPTS_R11_POLYLINE_HAS_FLAG)
{
if (flag_r11 & FLAG_R11_HAS_PSPACE)
{
extra_r11 = bit_read_RC (dat);
LOG_TRACE (", extra_r11: 0x%x", extra_r11);
}
if (flag_r11 & FLAG_R11_HAS_COLOR)
dat->byte += 1;
if (flag_r11 & FLAG_R11_HAS_LTYPE)
{
PRE (R_11)
{
dat->byte += 1;
}
else dat->byte += 2;
}
if (flag_r11 & FLAG_R11_HAS_THICKNESS)
dat->byte += 8;
if (flag_r11 & FLAG_R11_HAS_ELEVATION)
dat->byte += 8;
if (extra_r11 & EXTRA_R11_HAS_EED)
{
eed_size = bit_read_RS (dat);
LOG_TRACE (", eed_size: %d", eed_size);
dat->byte += eed_size;
}
if (flag_r11 & FLAG_R11_HAS_HANDLING)
{
handling_len = bit_read_RC (dat);
LOG_TRACE (", handling_len: %d", handling_len);
dat->byte += handling_len;
}
if (extra_r11 & EXTRA_R11_HAS_VIEWPORT)
dat->byte += 2;
pline_flag = bit_read_RC (dat);
LOG_TRACE (", pline_flag: 0x%x", pline_flag);
LOG_POS;
dat->byte = start_byte;
if (pline_flag & FLAG_POLYLINE_3D)
error |= dwg_decode_POLYLINE_3D (dat, obj);
else if (pline_flag & FLAG_POLYLINE_MESH)
error |= dwg_decode_POLYLINE_MESH (dat, obj);
else if (pline_flag & FLAG_POLYLINE_PFACE_MESH)
error |= dwg_decode_POLYLINE_PFACE (dat, obj);
else
error |= dwg_decode_POLYLINE_2D (dat, obj);
}
else
{
dat->byte = start_byte;
LOG_TRACE ("\n");
error |= dwg_decode_POLYLINE_2D (dat, obj);
}
}
break;
case DWG_TYPE_VERTEX_r11:
{ // which vertex?
BITCODE_RC flag_r11;
BITCODE_RS opts_r11;
BITCODE_RC extra_r11 = 0;
BITCODE_RS eed_size;
BITCODE_RC handling_len;
BITCODE_RC vertex_flag;
size_t start_byte;
LOG_TRACE ("Detect vertex:");
start_byte = dat->byte;
LOG_TRACE (" start_byte: %" PRIuSIZE ",", start_byte);
flag_r11 = bit_read_RC (dat);
LOG_TRACE (" flag_r11: 0x%x,", flag_r11);
dat->byte += 4;
opts_r11 = bit_read_RS (dat);
LOG_TRACE (" opts_r11: 0x%x", opts_r11);
if (flag_r11 & FLAG_R11_HAS_COLOR)
dat->byte += 1;
if (flag_r11 & FLAG_R11_HAS_LTYPE)
{
PRE (R_11)
{
dat->byte += 1;
}
else dat->byte += 2;
}
if (flag_r11 & FLAG_R11_HAS_THICKNESS)
dat->byte += 8;
if (flag_r11 & FLAG_R11_HAS_ELEVATION)
dat->byte += 8;
if (flag_r11 & FLAG_R11_HAS_PSPACE)
{
extra_r11 = bit_read_RC (dat);
LOG_TRACE (", extra_r11: 0x%x", extra_r11);
}
if (extra_r11 && extra_r11 & EXTRA_R11_HAS_EED)
{
eed_size = bit_read_RS (dat);
LOG_TRACE (", eed_size: %d", eed_size);
dat->byte += eed_size;
}
if (flag_r11 & FLAG_R11_HAS_HANDLING)
{
handling_len = bit_read_RC (dat);
LOG_TRACE (", handling_len: %d", handling_len);
dat->byte += handling_len;
}
if (extra_r11 && extra_r11 & EXTRA_R11_HAS_VIEWPORT)
dat->byte += 2;
if (!(opts_r11 & OPTS_R11_VERTEX_HAS_NOT_X_Y))
dat->byte += 16;
if (opts_r11 & OPTS_R11_VERTEX_HAS_START_WIDTH)
dat->byte += 8;
if (opts_r11 & OPTS_R11_VERTEX_HAS_END_WIDTH)
dat->byte += 8;
if (opts_r11 & OPTS_R11_VERTEX_HAS_BULGE)
dat->byte += 8;
if (opts_r11 & OPTS_R11_VERTEX_HAS_FLAG)
{
vertex_flag = bit_read_RC (dat);
LOG_TRACE (", vertex_flag: 0x%x", vertex_flag);
LOG_POS;
dat->byte = start_byte;
if (vertex_flag & FLAG_VERTEX_MESH
&& vertex_flag & FLAG_VERTEX_PFACE_MESH)
error |= dwg_decode_VERTEX_PFACE (dat, obj);
else if (vertex_flag & FLAG_VERTEX_MESH)
error |= dwg_decode_VERTEX_MESH (dat, obj);
else if (vertex_flag & FLAG_VERTEX_PFACE_MESH)
error |= dwg_decode_VERTEX_PFACE_FACE (dat, obj);
else if (vertex_flag & FLAG_VERTEX_3D)
error |= dwg_decode_VERTEX_3D (dat, obj);
else
error |= dwg_decode_VERTEX_2D (dat, obj);
}
else
{
dat->byte = start_byte;
LOG_TRACE ("\n");
error |= dwg_decode_VERTEX_2D (dat, obj);
}
}
break;
case DWG_TYPE_3DLINE_r11:
error |= dwg_decode__3DLINE (dat, obj);
break;
case DWG_TYPE_3DFACE_r11:
error |= dwg_decode__3DFACE (dat, obj);
break;
case DWG_TYPE_DIMENSION_r11:
error |= decode_preR13_DIMENSION (dat, obj);
break;
case DWG_TYPE_VIEWPORT_r11:
error |= dwg_decode_VIEWPORT (dat, obj);
break;
default:
dat->byte--;
DEBUG_HERE;
obj->type
= (BITCODE_BS)(DWG_TYPE_UNKNOWN_r11 | (obj->type & 0x80));
obj->fixedtype = DWG_TYPE_UNKNOWN_ENT;
error |= DWG_ERR_SECTIONNOTFOUND;
dat->byte++;
if (dat->byte >= dat->size)
{
LOG_ERROR ("%s buffer overflow at %" PRIuSIZE
".%u >= %" PRIuSIZE,
__FUNCTION__, dat->byte, dat->bit, dat->size);
return DWG_ERR_INVALIDDWG;
}
break;
}
assert (!dat->bit);
PRE (R_2_0b)
{
obj->size = (dat->byte - obj->address) & 0xFFFFFFFF;
if (num + 1 > dwg->num_objects)
break;
}
if (obj->type != DWG_TYPE_JUMP_r11)
{
SINCE (R_2_0b) // Pre R_2_0 doesn't contain size of entity
{
PRE (R_11) // no crc16
{
// tolerate trailing entity bytes (kept in unknown_rest)
// below
if (obj->size > dat->size - obj->address
|| obj->size + obj->address > dat->byte + 1 + 64)
{
LOG_ERROR ("Invalid obj->size " FORMAT_RL
" changed to %" PRIuSIZE,
obj->size, dat->byte - obj->address);
error |= DWG_ERR_VALUEOUTOFBOUNDS;
obj->size = (dat->byte - obj->address) & 0xFFFFFFFF;
}
else if (obj->address + obj->size != dat->byte)
{
LOG_ERROR ("offset %ld",
(long)(obj->address + obj->size - dat->byte));
if (obj->address + obj->size > dat->byte)
{
BITCODE_RL offset
= (BITCODE_RL)(obj->address + obj->size
- dat->byte);
obj->num_unknown_rest = 8 * offset;
obj->unknown_rest = (BITCODE_TF)calloc (offset, 1);
if (obj->unknown_rest)
{
memcpy (obj->unknown_rest,
&dat->chain[dat->byte], offset);
LOG_TRACE_TF (obj->unknown_rest, offset);
}
else
{
LOG_ERROR ("Out of memory");
obj->num_unknown_rest = 0;
}
}
if (obj->size > 2)
dat->byte = obj->address + obj->size;
}
}
LATER_VERSIONS
{
// tolerate trailing entity bytes (kept in unknown_rest)
// below
if (obj->size > dat->size - obj->address
|| obj->size + obj->address > dat->byte + 2 + 64)
{
LOG_ERROR ("Invalid obj->size " FORMAT_RL
" changed to %" PRIuSIZE,
obj->size, dat->byte + 2 - obj->address);
error |= DWG_ERR_VALUEOUTOFBOUNDS;
obj->size
= ((dat->byte + 2) - obj->address) & 0xFFFFFFFF;
}
else if (obj->address + obj->size != dat->byte + 2)
{
LOG_ERROR ("offset %ld", (long)(obj->address + obj->size
- (dat->byte + 2)));
if (obj->address + obj->size > dat->byte + 2)
{
BITCODE_RL offset
= (BITCODE_RL)(obj->address + obj->size
- (dat->byte + 2));
obj->num_unknown_rest = 8 * offset;
obj->unknown_rest = bit_read_TF (dat, offset);
if (obj->unknown_rest)
{
LOG_TRACE_TF (obj->unknown_rest, offset);
}
else
{
LOG_ERROR ("Out of memory");
obj->num_unknown_rest = 0;
}
}
if (obj->address + obj->size >= start && start > 60
&& obj->address + obj->size - 2 > dat->byte)
dat->byte = obj->address + obj->size - 2;
}
if (!bit_check_CRC (dat, obj->address, 0xC0C1))
error |= DWG_ERR_WRONGCRC;
}
}
}
// add to block header
if (_hdr && obj->supertype == DWG_SUPERTYPE_ENTITY && obj->tio.entity
&& obj->fixedtype != DWG_TYPE_UNUSED
&& obj->fixedtype != DWG_TYPE_JUMP
&& obj->type != DWG_TYPE_VERTEX_r11
&& obj->fixedtype != DWG_TYPE_SEQEND)
{
BITCODE_H ref;
Dwg_Object *ehdr = &dwg->object[hdr_index];
Dwg_Object_BLOCK_HEADER *ehdr_data = _hdr;
BITCODE_RLL ehdr_handle = hdr_handle;
if (ps_hdr && obj->tio.entity->entmode == 1)
{
ehdr = &dwg->object[ps_hdr_index];
ehdr_data = ps_hdr;
ehdr_handle = ps_hdr_handle;
}
if (!obj->handle.value)
obj->handle.value = dwg_next_handle (dwg);
ref = dwg_add_handleref (dwg, 3, obj->handle.value, ehdr);
// if (dwg->dirty_refs)
// find ehdr_data again from ehdr_handle
LOG_TRACE ("BLOCK_HEADER \"%s\".", ehdr_data->name);
if (obj->fixedtype != DWG_TYPE_BLOCK)
PUSH_HV (ehdr_data, num_owned, entities, ref);
obj->tio.entity->ownerhandle
= dwg_add_handleref (dwg, 4, ehdr_handle, obj);
obj->tio.entity->ownerhandle->r11_idx = block_idx;
LOG_TRACE ("ownerhandle: " FORMAT_HREF11 "\n",
ARGS_HREF11 (obj->tio.entity->ownerhandle));
}
num++;
if (dat->byte < oldpos + size)
LOG_TRACE ("\n");
if (dat->byte >= dat->size && (BITCODE_RL)dat->byte != end)
{
LOG_ERROR ("Too many entities, buffer overflow %" PRIuSIZE
" >= %" PRIuSIZE,
dat->byte, dat->size);
return DWG_ERR_INVALIDDWG;
}
if (dat->byte == iter_start)
{
LOG_ERROR (
"No advance in decode_preR13_entities, abort at %" PRIuSIZE,
dat->byte);
return DWG_ERR_INVALIDDWG;
}
}
if ((BITCODE_RL)dat->byte != end)
{
LOG_ERROR ("@0x%zx => end 0x%x", dat->byte, end);
return DWG_ERR_INVALIDDWG;
}
}
SINCE (R_11)
{
switch (entity_section)
{
case ENTITIES_SECTION_INDEX:
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_ENTITIES_END);
break;
case BLOCKS_SECTION_INDEX:
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_BLOCK_ENTITIES_END);
break;
case EXTRAS_SECTION_INDEX:
DECODE_PRER13_SENTINEL (DWG_SENTINEL_R11_EXTRA_ENTITIES_END);
break;
default:
LOG_ERROR ("Internal error: Illegal entity_section %d 0-2\n",
(int)entity_section);
return DWG_ERR_INTERNALERROR;
}
}
LOG_INFO ("==========================================\n");
LOG_TRACE ("%s: end\n", entities_section[entity_section]);
return error;
}
#undef IS_DECODER