static inline INT get_element_size(const region_element* element) { INT needed = sizeof(DWORD); /* DWORD for the type */ switch(element->type) { case RegionDataRect: return needed + sizeof(GpRect); case RegionDataPath: { const GpPath *path = element->elementdata.path; DWORD flags = is_integer_path(path) ? FLAGS_INTPATH : FLAGS_NOFLAGS; /* 3 for headers, once again size doesn't count itself */ needed += sizeof(DWORD) * 3; if (flags & FLAGS_INTPATH) needed += 2 * sizeof(SHORT) * path->pathdata.Count; else needed += 2 * sizeof(FLOAT) * path->pathdata.Count; needed += get_pathtypes_size(path); needed += sizeof(DWORD); /* Extra DWORD for pathheader.size */ return needed; } case RegionDataEmptyRect: case RegionDataInfiniteRect: return needed; default: needed += get_element_size(element->elementdata.combine.left); needed += get_element_size(element->elementdata.combine.right); return needed; } return 0; }
EbmlElement * kax_file_c::read_one_element() { if (m_segment_end && (m_in->getFilePointer() >= m_segment_end)) return nullptr; int upper_lvl_el = 0; EbmlElement *l1 = m_es->FindNextElement(EBML_CLASS_CONTEXT(KaxSegment), upper_lvl_el, 0xFFFFFFFFL, true); if (!l1) return nullptr; const EbmlCallbacks *callbacks = find_ebml_callbacks(EBML_INFO(KaxSegment), EbmlId(*l1)); if (!callbacks) callbacks = &EBML_CLASS_CALLBACK(KaxSegment); EbmlElement *l2 = nullptr; try { l1->Read(*m_es.get(), EBML_INFO_CONTEXT(*callbacks), upper_lvl_el, l2, true); } catch (libebml::CRTError &e) { mxdebug_if(m_debug_resync, boost::format("exception reading element data: %1% (%2%)\n") % e.what() % e.getError()); m_in->setFilePointer(l1->GetElementPosition() + 1); delete l1; return nullptr; } unsigned long element_size = get_element_size(l1); if (m_debug_resync) mxinfo(boost::format("kax_file::read_one_element(): read element at %1% calculated size %2% stored size %3%\n") % l1->GetElementPosition() % element_size % (l1->IsFiniteSize() ? (boost::format("%1%") % l1->ElementSize()).str() : std::string("unknown"))); m_in->setFilePointer(l1->GetElementPosition() + element_size, seek_beginning); return l1; }
/* Common code for CombineRegion* * All the caller has to do is get its format into an element */ static inline void fuse_region(GpRegion* region, region_element* left, region_element* right, const CombineMode mode) { region->node.type = mode; region->node.elementdata.combine.left = left; region->node.elementdata.combine.right = right; region->header.size = sizeheader_size + get_element_size(®ion->node); region->header.num_children += 2; }
/* Does not check parameters, caller must do that */ static inline GpStatus init_region(GpRegion* region, const RegionType type) { region->node.type = type; region->header.checksum = 0xdeadbeef; region->header.magic = VERSION_MAGIC; region->header.num_children = 0; region->header.size = sizeheader_size + get_element_size(®ion->node); return Ok; }
/***************************************************************************** * GdipGetRegionDataSize [GDIPLUS.@] */ GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed) { TRACE("%p, %p\n", region, needed); if (!(region && needed)) return InvalidParameter; /* header.size doesn't count header.size and header.checksum */ *needed = sizeof(DWORD) * 2 + sizeheader_size + get_element_size(®ion->node); return Ok; }
static inline INT get_element_size(const region_element* element) { INT needed = sizeof(DWORD); /* DWORD for the type */ switch(element->type) { case RegionDataRect: return needed + sizeof(GpRect); case RegionDataPath: needed += element->elementdata.pathdata.pathheader.size; needed += sizeof(DWORD); /* Extra DWORD for pathheader.size */ return needed; case RegionDataEmptyRect: case RegionDataInfiniteRect: return needed; default: needed += get_element_size(element->elementdata.combine.left); needed += get_element_size(element->elementdata.combine.right); return needed; } return 0; }
unsigned long kax_file_c::get_element_size(EbmlElement *e) { auto m = dynamic_cast<EbmlMaster *>(e); if (!m || e->IsFiniteSize()) return e->GetSizeLength() + EBML_ID_LENGTH(static_cast<const EbmlId &>(*e)) + e->GetSize(); auto max_end_pos = e->GetElementPosition() + EBML_ID_LENGTH(static_cast<const EbmlId &>(*e)); for (int idx = 0, end = m->ListSize(); end > idx; ++idx) max_end_pos = std::max(max_end_pos, (*m)[idx]->GetElementPosition() + get_element_size((*m)[idx])); return max_end_pos - e->GetElementPosition(); }
DWORD write_region_data(const GpRegion *region, void *data) { struct region_header *header = data; INT filled = 0; DWORD size; size = sizeof(struct region_header) + get_element_size(®ion->node); if (!data) return size; header->magic = VERSION_MAGIC2; header->num_children = region->num_children; filled += 2; /* With few exceptions, everything written is DWORD aligned, * so use that as our base */ write_element(®ion->node, (DWORD*)data, &filled); return size; }
/***************************************************************************** * GdipGetRegionData [GDIPLUS.@] * * Returns the header, followed by combining ops and region elements. * * PARAMS * region [I] region to retrieve from * buffer [O] buffer to hold the resulting data * size [I] size of the buffer * needed [O] (optional) how much data was written * * RETURNS * SUCCESS: Ok * FAILURE: InvalidParameter * * NOTES * The header contains the size, a checksum, a version string, and the number * of children. The size does not count itself or the checksum. * Version is always something like 0xdbc01001 or 0xdbc01002 * * An element is a RECT, or PATH; Combining ops are stored as their * CombineMode value. Special regions (infinite, empty) emit just their * op-code; GpRectFs emit their code followed by their points; GpPaths emit * their code followed by a second header for the path followed by the actual * path data. Followed by the flags for each point. The pathheader contains * the size of the data to follow, a version number again, followed by a count * of how many points, and any special flags which may apply. 0x4000 means its * a path of shorts instead of FLOAT. * * Combining Ops are stored in reverse order from when they were constructed; * the output is a tree where the left side combining area is always taken * first. */ GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size, UINT *needed) { struct _region_header { DWORD size; DWORD checksum; DWORD magic; DWORD num_children; } *region_header; INT filled = 0; UINT required; GpStatus status; TRACE("%p, %p, %d, %p\n", region, buffer, size, needed); if (!region || !buffer || !size) return InvalidParameter; status = GdipGetRegionDataSize(region, &required); if (status != Ok) return status; if (size < required) { if (needed) *needed = size; return InsufficientBuffer; } region_header = (struct _region_header *)buffer; region_header->size = sizeheader_size + get_element_size(®ion->node); region_header->checksum = 0; region_header->magic = VERSION_MAGIC; region_header->num_children = region->num_children; filled += 4; /* With few exceptions, everything written is DWORD aligned, * so use that as our base */ write_element(®ion->node, (DWORD*)buffer, &filled); if (needed) *needed = filled * sizeof(DWORD); return Ok; }
EbmlElement * kax_file_c::read_next_level1_element_internal(uint32_t wanted_id) { if (m_segment_end && (m_in->getFilePointer() >= m_segment_end)) return nullptr; m_resynced = false; m_resync_start_pos = 0; // Read the next ID. int64_t search_start_pos = m_in->getFilePointer(); vint_c actual_id = vint_c::read_ebml_id(m_in); m_in->setFilePointer(search_start_pos, seek_beginning); if (m_debug_read_next) mxinfo(boost::format("kax_file::read_next_level1_element(): search at %1% for %|4$x| act id %|2$x| is_valid %3%\n") % search_start_pos % actual_id.m_value % actual_id.is_valid() % wanted_id); // If no valid ID was read then re-sync right away. No other tests // can be run. if (!actual_id.is_valid()) return resync_to_level1_element(wanted_id); // Easiest case: next level 1 element following the previous one // without any element inbetween. if ( (wanted_id == actual_id.m_value) || ( (0 == wanted_id) && ( is_level1_element_id(actual_id) || is_global_element_id(actual_id)))) { EbmlElement *l1 = read_one_element(); if (l1) return l1; } // If a specific level 1 is wanted then look for next ID by skipping // other level 1 or special elements. If that files fallback to a // byte-for-byte search for the ID. if ((0 != wanted_id) && (is_level1_element_id(actual_id) || is_global_element_id(actual_id))) { m_in->setFilePointer(search_start_pos, seek_beginning); EbmlElement *l1 = read_one_element(); if (l1) { int64_t element_size = get_element_size(l1); bool ok = (0 != element_size) && m_in->setFilePointer2(l1->GetElementPosition() + element_size, seek_beginning); if (m_debug_read_next) mxinfo(boost::format("kax_file::read_next_level1_element(): other level 1 element %1% new pos %2% fsize %3% epos %4% esize %5%\n") % EBML_NAME(l1) % (l1->GetElementPosition() + element_size) % m_file_size % l1->GetElementPosition() % element_size); delete l1; return ok ? read_next_level1_element(wanted_id) : nullptr; } } // Last case: no valid ID found. Try a byte-for-byte search for the // next wanted/level 1 ID. Also try to locate at least three valid // ID/sizes, not just one ID. m_in->setFilePointer(search_start_pos, seek_beginning); return resync_to_level1_element(wanted_id); }
/***************************************************************************** * GdipCreateRegionPath [GDIPLUS.@] * * Creates a GpRegion from a GpPath * * PARAMS * path [I] path to base the region on * region [O] pointer to the newly allocated region * * RETURNS * SUCCESS: Ok * FAILURE: InvalidParameter * * NOTES * If a path has no floating point points, its points will be stored as shorts * (INTPATH) * * If a path is empty, it is considered to be an INTPATH */ GpStatus WINGDIPAPI GdipCreateRegionPath(GpPath *path, GpRegion **region) { region_element* element; GpPoint *pointsi; GpPointF *pointsf; GpStatus stat; DWORD flags = FLAGS_INTPATH; INT count, i; TRACE("%p, %p\n", path, region); if (!(path && region)) return InvalidParameter; *region = GdipAlloc(sizeof(GpRegion)); if(!*region) return OutOfMemory; stat = init_region(*region, RegionDataPath); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } element = &(*region)->node; count = path->pathdata.Count; /* Test to see if the path is an Integer path */ if (count) { pointsi = GdipAlloc(sizeof(GpPoint) * count); pointsf = GdipAlloc(sizeof(GpPointF) * count); if (!(pointsi && pointsf)) { GdipFree(pointsi); GdipFree(pointsf); GdipDeleteRegion(*region); return OutOfMemory; } stat = GdipGetPathPointsI(path, pointsi, count); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } stat = GdipGetPathPoints(path, pointsf, count); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } for (i = 0; i < count; i++) { if (!(pointsi[i].X == pointsf[i].X && pointsi[i].Y == pointsf[i].Y )) { flags = FLAGS_NOFLAGS; break; } } GdipFree(pointsi); GdipFree(pointsf); } stat = GdipClonePath(path, &element->elementdata.pathdata.path); if (stat != Ok) { GdipDeleteRegion(*region); return stat; } /* 3 for headers, once again size doesn't count itself */ element->elementdata.pathdata.pathheader.size = ((sizeof(DWORD) * 3)); switch(flags) { /* Floats, sent out as floats */ case FLAGS_NOFLAGS: element->elementdata.pathdata.pathheader.size += (sizeof(DWORD) * count * 2); break; /* INTs, sent out as packed shorts */ case FLAGS_INTPATH: element->elementdata.pathdata.pathheader.size += (sizeof(DWORD) * count); break; default: FIXME("Unhandled flags (%08x). Expect wrong results.\n", flags); } element->elementdata.pathdata.pathheader.size += get_pathtypes_size(path); element->elementdata.pathdata.pathheader.magic = VERSION_MAGIC; element->elementdata.pathdata.pathheader.count = count; element->elementdata.pathdata.pathheader.flags = flags; (*region)->header.size = sizeheader_size + get_element_size(element); return Ok; }