/* * Copyright 2002-2012, Haiku. All Rights Reserved. * This file may be used under the terms of the MIT License. * * Author: Zousar Shaker * Axel Dörfler, axeld@pinc-software.de * Marcus Overhagen */ /*! Implements the following classes: BParameterWeb, BParameterGroup, BParameter, BNullParameter, BContinuousParameter, BDiscreteParameter */ #include #include #include #include #include #include "DataExchange.h" #include "MediaDebug.h" #include "MediaMisc.h" /* The following is documentation on the flattened format of structures/classes in this module: //--------BEGIN-CORE-BPARAMETER-STRUCT--------------------- ?? (0x02040607): 4 bytes BParameter Struct Size (in bytes): 4 bytes ID: 4 bytes Name String Length: 1 byte (??) Name String: 'Name String Length' bytes Kind String Length: 1 byte (??) Kind String: 'Kind String Length' bytes Unit String Length: 1 byte (??) Unit String: 'Unit String Length' bytes Inputs Count: 4 bytes Inputs (pointers): ('Inputs Count')*4 bytes Outputs Count: 4 bytes Outputs (pointers): ('Outputs Count')*4 bytes Media Type: 4 bytes ChannelCount: 4 bytes Flags: 4 bytes //---------END-CORE-BPARAMETER-STRUCT----------------------- //--------BEGIN-BCONTINUOUSPARAMETER-STRUCT--------- Min: 4 bytes (as float) Max: 4 bytes (as float) Stepping: 4 bytes (as float) Response: 4 bytes (as int or enum) Factor: 4 bytes (as float) Offset: 4 bytes (as float) //--------END-BCONTINUOUSPARAMETER-STRUCT------------- //--------BEGIN-BDISCRETEPARAMETER-STRUCT---------------- NumItems: 4 bytes (as int) //for each item BEGIN Item Name String Length: 1 byte Item Name String: 'Item Name String Length' bytes Item Value: 4 bytes (as int) //for each item END //--------END-BDISCRETEPARAMETER-STRUCT------------------- //--------BEGIN-CORE-BPARAMETERGROUP-STRUCT----------- ?? (0x03040507 OR 0x03040509 depending if the flags field is included or not???): 4 bytes (possible) Flags: 4 bytes Name String Length: 1 byte (??) Name String: 'Name String Length' bytes Param Count: 4 bytes //for each Param BEGIN Pointer: 4 bytes Parameter Type: 4 bytes Flattened Parameter Size: 4 bytes Flattened Parameter: 'Flattened Parameter Size' bytes //for each Param END Subgroup Count: 4 bytes //for each SubGroup BEGIN Pointer: 4 bytes MEDIA PARAMETER GROUP TYPE('BMCG' (opposite byte order in file)): 4 bytes Flattened Group Size: 4 bytes Flattened Group: 'Flattened Group Size' bytes //for each SubGroup END //---------END-CORE-BPARAMETERGROUP-STRUCT-------------- //--------BEGIN-CORE-BPARAMETERWEB-STRUCT----------- ?? 0x01030506: 4 bytes ??: 4 bytes (is always 1) Group Count: 4 bytes Node (as media_node): 0x18 bytes (decimal 24 bytes) //for each Group BEGIN Flattened Group Size: 4 bytes Flattened Group: 'Flattened Group Size' bytes //for each Group END //for each Group BEGIN ??: 4 bytes (never get written to (holds uninitialized value)) //for each Group END //---------END-CORE-BPARAMETERWEB-STRUCT-------------- */ const char * const B_GENERIC = ""; const char * const B_MASTER_GAIN = "Master"; const char * const B_GAIN = "Gain"; const char * const B_BALANCE = "Balance"; const char * const B_FREQUENCY = "Frequency"; const char * const B_LEVEL = "Level"; const char * const B_SHUTTLE_SPEED = "Speed"; const char * const B_CROSSFADE = "XFade"; const char * const B_EQUALIZATION = "EQ"; const char * const B_COMPRESSION = "Compression"; const char * const B_QUALITY = "Quality"; const char * const B_BITRATE = "Bitrate"; const char * const B_GOP_SIZE = "GOPSize"; const char * const B_MUTE = "Mute"; const char * const B_ENABLE = "Enable"; const char * const B_INPUT_MUX = "Input"; const char * const B_OUTPUT_MUX = "Output"; const char * const B_TUNER_CHANNEL = "Channel"; const char * const B_TRACK = "Track"; const char * const B_RECSTATE = "RecState"; const char * const B_SHUTTLE_MODE = "Shuttle"; const char * const B_RESOLUTION = "Resolution"; const char * const B_COLOR_SPACE = "Colorspace"; const char * const B_FRAME_RATE = "FrameRate"; const char * const B_VIDEO_FORMAT = "VideoFormat"; const char * const B_WEB_PHYSICAL_INPUT = "PhysInput"; const char * const B_WEB_PHYSICAL_OUTPUT = "PhysOutput"; const char * const B_WEB_ADC_CONVERTER = "ADC"; const char * const B_WEB_DAC_CONVERTER = "DAC"; const char * const B_WEB_LOGICAL_INPUT = "LogInput"; const char * const B_WEB_LOGICAL_OUTPUT = "LogOutput"; const char * const B_WEB_LOGICAL_BUS = "LogBus"; const char * const B_WEB_BUFFER_INPUT = "DataInput"; const char * const B_WEB_BUFFER_OUTPUT = "DataOutput"; const char * const B_SIMPLE_TRANSPORT = "SimpleTransport"; // Flattened data static const int32 kCurrentParameterWebVersion = 1; static const uint32 kParameterWebMagic = 0x01030506; static const uint32 kBufferGroupMagic = 0x03040509; static const uint32 kBufferGroupMagicNoFlags = 0x03040507; static const uint32 kParameterMagic = 0x02040607; static const ssize_t kAdditionalParameterGroupSize = 12; static const ssize_t kAdditionalParameterSize = 23 + 3 * sizeof(ssize_t); /* BContinuousParameter - FlattenedSize() fixed part * Min: 4 bytes (as float) * Max: 4 bytes (as float) * Stepping: 4 bytes (as float) * Response: 4 bytes (as int or enum) * Factor: 4 bytes (as float) * Offset: 4 bytes (as float) */ static const ssize_t kAdditionalContinuousParameterSize = 5 * sizeof(float) + sizeof(BContinuousParameter::response); static const ssize_t kAdditionalDiscreteParameterSize = sizeof(ssize_t); // helper functions template Type read_from_buffer(const void **_buffer) { const Type *typedBuffer = static_cast(*_buffer); Type value = *typedBuffer; typedBuffer++; *_buffer = static_cast(typedBuffer); return value; } static status_t read_string_from_buffer(const void **_buffer, char **_string, ssize_t size) { if (size < 1) return B_BAD_VALUE; const uint8 *buffer = static_cast(*_buffer); uint8 length = *buffer++; if (length > size - 1) return B_BAD_VALUE; char *string = (char *)malloc(length + 1); if (string == NULL) return B_NO_MEMORY; memcpy(string, buffer, length); string[length] = '\0'; free(*_string); *_buffer = static_cast(buffer + length); *_string = string; return B_OK; } // currently unused #if 0 template Type * reserve_in_buffer(void **_buffer) { Type *typedBuffer = static_cast(*_buffer); *typedBuffer = 0; typedBuffer++; *_buffer = static_cast(typedBuffer); } #endif template void write_to_buffer(void **_buffer, Type value) { Type *typedBuffer = static_cast(*_buffer); *typedBuffer = value; typedBuffer++; *_buffer = static_cast(typedBuffer); } void write_string_to_buffer(void **_buffer, const char *string) { uint8 *buffer = static_cast(*_buffer); uint32 length = string ? strlen(string) : 0; if (length > 255) length = 255; *buffer++ = static_cast(length); if (length) { memcpy(buffer, string, length); buffer += length; } *_buffer = static_cast(buffer); } static void skip_in_buffer(const void **_buffer, uint32 bytes) { const uint8 *buffer = static_cast(*_buffer); buffer += bytes; *_buffer = static_cast(buffer); } static void inline skip_in_buffer(void **_buffer, uint32 bytes) { // This actually shouldn't be necessary, but I believe it's a // bug in Be's gcc - it complains about "adds cv-quals without intervening `const'" // when passing a "const void **" pointer as the first argument. // Maybe I am just stupid, though -- axeld. skip_in_buffer((const void **)_buffer, bytes); } template Type swap32(Type value, bool doSwap) { STATIC_ASSERT(sizeof(Type) == 4); if (doSwap) return (Type)B_SWAP_INT32((int32)value); return value; } template Type swap64(Type value, bool doSwap) { STATIC_ASSERT(sizeof(Type) == 8); if (doSwap) return (Type)B_SWAP_INT64((int64)value); return value; } template Type read_from_buffer_swap32(const void **_buffer, bool doSwap) { return swap32(read_from_buffer(_buffer), doSwap); } template Type read_pointer_from_buffer_swap(const void **_buffer, bool doSwap) { #if B_HAIKU_32_BIT return swap32(read_from_buffer(_buffer), doSwap); #elif B_HAIKU_64_BIT return swap64(read_from_buffer(_buffer), doSwap); #else # error Interesting #endif } static inline ssize_t size_left(ssize_t size, const void *bufferStart, const void *buffer) { return size - static_cast((const uint8 *)buffer - (const uint8 *)bufferStart); } // #pragma mark - BParameterWeb BParameterWeb::BParameterWeb() : fNode(media_node::null) // fNode is set in BControllable::SetParameterWeb() { CALLED(); fGroups = new BList(); fOldRefs = new BList(); fNewRefs = new BList(); } BParameterWeb::~BParameterWeb() { CALLED(); for (int32 i = fGroups->CountItems(); i-- > 0;) { delete static_cast(fGroups->ItemAt(i)); } delete fGroups; delete fOldRefs; delete fNewRefs; } media_node BParameterWeb::Node() { return fNode; } BParameterGroup* BParameterWeb::MakeGroup(const char* name) { CALLED(); BParameterGroup* group = new(std::nothrow) BParameterGroup(this, name); if (group == NULL) return NULL; if (!fGroups->AddItem(group)) { delete group; return NULL; } return group; } int32 BParameterWeb::CountGroups() { return fGroups->CountItems(); } BParameterGroup* BParameterWeb::GroupAt(int32 index) { return static_cast(fGroups->ItemAt(index)); } int32 BParameterWeb::CountParameters() { CALLED(); // Counts over all groups (and sub-groups) in the web. // The "groups" list is used as count stack BList groups(*fGroups); int32 count = 0; for (int32 i = 0; i < groups.CountItems(); i++) { BParameterGroup* group = static_cast(groups.ItemAt(i)); count += group->CountParameters(); if (group->fGroups != NULL) groups.AddList(group->fGroups); } return count; } BParameter* BParameterWeb::ParameterAt(int32 index) { CALLED(); // Iterates over all groups (and sub-groups) in the web. // The "groups" list is used as iteration stack (breadth search style) // Maintains the same order as the Be implementation BList groups(*fGroups); for (int32 i = 0; i < groups.CountItems(); i++) { BParameterGroup* group = static_cast(groups.ItemAt(i)); int32 count = group->CountParameters(); if (index < count) return group->ParameterAt(index); index -= count; // the index is always relative to the start of the current group if (group->fGroups != NULL) groups.AddList(group->fGroups); } TRACE("*** could not find parameter at %" B_PRId32 " (count = %" B_PRId32 ")\n", index, CountParameters()); return NULL; } bool BParameterWeb::IsFixedSize() const { return false; } type_code BParameterWeb::TypeCode() const { return B_MEDIA_PARAMETER_WEB_TYPE; } ssize_t BParameterWeb::FlattenedSize() const { CALLED(); /* //--------BEGIN-CORE-BPARAMETERWEB-STRUCT----------- ?? 0x01030506: 4 bytes ??: 4 bytes (is always 1) Group Count: 4 bytes Node (as media_node): 0x18 bytes (decimal 24 bytes) //for each Group BEGIN Flattened Group Size: 4 bytes Flattened Group: 'Flattened Group Size' bytes //for each Group END //for each Group BEGIN ??: 4 bytes (never get written to (holds uninitialized value)) //for each Group END //---------END-CORE-BPARAMETERWEB-STRUCT-------------- */ //36 guaranteed bytes, variable after that. ssize_t size = sizeof(int32) + 2 * sizeof(int32) + sizeof(media_node); for (int32 i = fGroups->CountItems(); i-- > 0;) { BParameterGroup* group = static_cast(fGroups->ItemAt(i)); if (group != NULL) { size += sizeof(ssize_t) + group->FlattenedSize(); } } return size; } status_t BParameterWeb::Flatten(void* buffer, ssize_t size) const { CALLED(); if (buffer == NULL) return B_NO_INIT; ssize_t actualSize = BParameterWeb::FlattenedSize(); if (size < actualSize) return B_NO_MEMORY; void* bufferStart = buffer; write_to_buffer(&buffer, kParameterWebMagic); write_to_buffer(&buffer, kCurrentParameterWebVersion); // flatten all groups into this buffer int32 count = fGroups->CountItems(); write_to_buffer(&buffer, count); write_to_buffer(&buffer, fNode); for (int32 i = 0; i < count; i++) { BParameterGroup* group = static_cast(fGroups->ItemAt(i)); if (group == NULL) { ERROR("BParameterWeb::Flatten(): group is NULL\n"); continue; } ssize_t groupSize = group->FlattenedSize(); if (groupSize > size_left(size, bufferStart, buffer)) { ERROR("BParameterWeb::Flatten(): buffer too small\n"); return B_BAD_VALUE; } write_to_buffer(&buffer, groupSize); // write the flattened sub group status_t status = group->Flatten(buffer, groupSize); if (status < B_OK) return status; skip_in_buffer(&buffer, groupSize); } return B_OK; } bool BParameterWeb::AllowsTypeCode(type_code code) const { return code == TypeCode(); } status_t BParameterWeb::Unflatten(type_code code, const void* buffer, ssize_t size) { CALLED(); if (!AllowsTypeCode(code)) { ERROR("BParameterWeb::Unflatten(): wrong type code\n"); return B_BAD_TYPE; } if (buffer == NULL) { ERROR("BParameterWeb::Unflatten(): NULL buffer pointer\n"); return B_NO_INIT; } // if the buffer is smaller than the size needed to read the // signature field, the mystery field, the group count, and the Node, then there is a problem if (size < static_cast(sizeof(int32) + sizeof(int32) + sizeof(ssize_t) + sizeof(media_node))) { ERROR("BParameterWeb::Unflatten(): size to small\n"); return B_ERROR; } const void* bufferStart = buffer; uint32 magic = read_from_buffer(&buffer); bool isSwapped = false; if (magic == B_SWAP_INT32(kParameterWebMagic)) { isSwapped = true; magic = B_SWAP_INT32(magic); } if (magic != kParameterWebMagic) return B_BAD_DATA; // Note, it's not completely sure that this field is the version // information - but it doesn't seem to have another purpose int32 version = read_from_buffer_swap32(&buffer, isSwapped); if (version != kCurrentParameterWebVersion) { ERROR("BParameterWeb::Unflatten(): wrong version %" B_PRId32 " (%" B_PRIx32 ")?!\n", version, version); return B_ERROR; } for (int32 i = 0; i < fGroups->CountItems(); i++) { delete static_cast(fGroups->ItemAt(i)); } fGroups->MakeEmpty(); int32 count = read_from_buffer_swap32(&buffer, isSwapped); fNode = read_from_buffer(&buffer); if (isSwapped) swap_data(B_INT32_TYPE, &fNode, sizeof(media_node), B_SWAP_ALWAYS); for (int32 i = 0; i < count; i++) { ssize_t groupSize = read_pointer_from_buffer_swap(&buffer, isSwapped); if (groupSize > size_left(size, bufferStart, buffer)) { ERROR("BParameterWeb::Unflatten(): buffer too small\n"); return B_BAD_DATA; } BParameterGroup* group = new BParameterGroup(this, "unnamed"); status_t status = group->Unflatten(group->TypeCode(), buffer, groupSize); if (status < B_OK) { ERROR("BParameterWeb::Unflatten(): unflatten group failed\n"); delete group; return status; } skip_in_buffer(&buffer, groupSize); fGroups->AddItem(group); } // fix all references (ParameterAt() style) BList groups(*fGroups); for (int32 i = 0; i < groups.CountItems(); i++) { BParameterGroup* group = static_cast(groups.ItemAt(i)); for (int32 index = group->CountParameters(); index-- > 0;) { BParameter* parameter = static_cast(group->ParameterAt(index)); parameter->FixRefs(*fOldRefs, *fNewRefs); } if (group->fGroups != NULL) groups.AddList(group->fGroups); } fOldRefs->MakeEmpty(); fNewRefs->MakeEmpty(); return B_OK; } void BParameterWeb::AddRefFix(void* oldItem, void* newItem) { fOldRefs->AddItem(oldItem); fNewRefs->AddItem(newItem); } // #pragma mark - BParameterGroup BParameterGroup::BParameterGroup(BParameterWeb* web, const char* name) : fWeb(web), fFlags(0) { CALLED(); TRACE("BParameterGroup: web = %p, name = \"%s\"\n", web, name); fName = strndup(name, 255); fControls = new BList(); fGroups = new BList(); } BParameterGroup::~BParameterGroup() { CALLED(); for (int i = fControls->CountItems(); i-- > 0;) { delete static_cast(fControls->ItemAt(i)); } delete fControls; for (int i = fGroups->CountItems(); i-- > 0;) { delete static_cast(fGroups->ItemAt(i)); } delete fGroups; free(fName); } BParameterWeb* BParameterGroup::Web() const { return fWeb; } const char* BParameterGroup::Name() const { return fName; } void BParameterGroup::SetFlags(uint32 flags) { fFlags = flags; } uint32 BParameterGroup::Flags() const { return fFlags; } BNullParameter* BParameterGroup::MakeNullParameter(int32 id, media_type mediaType, const char* name, const char* kind) { CALLED(); BNullParameter* parameter = new(std::nothrow) BNullParameter(id, mediaType, fWeb, name, kind); if (parameter == NULL) return NULL; parameter->fGroup = this; fControls->AddItem(parameter); return parameter; } BContinuousParameter* BParameterGroup::MakeContinuousParameter(int32 id, media_type mediaType, const char* name, const char* kind, const char* unit, float minimum, float maximum, float stepping) { CALLED(); BContinuousParameter* parameter = new(std::nothrow) BContinuousParameter(id, mediaType, fWeb, name, kind, unit, minimum, maximum, stepping); if (parameter == NULL) return NULL; parameter->fGroup = this; fControls->AddItem(parameter); return parameter; } BDiscreteParameter* BParameterGroup::MakeDiscreteParameter(int32 id, media_type mediaType, const char* name, const char* kind) { CALLED(); BDiscreteParameter* parameter = new(std::nothrow) BDiscreteParameter(id, mediaType, fWeb, name, kind); if (parameter == NULL) return NULL; parameter->fGroup = this; fControls->AddItem(parameter); return parameter; } BTextParameter* BParameterGroup::MakeTextParameter(int32 id, media_type mediaType, const char* name, const char* kind, size_t maxBytes) { CALLED(); BTextParameter* parameter = new(std::nothrow) BTextParameter(id, mediaType, fWeb, name, kind, maxBytes); if (parameter == NULL) return NULL; parameter->fGroup = this; fControls->AddItem(parameter); return parameter; } BParameterGroup* BParameterGroup::MakeGroup(const char* name) { CALLED(); BParameterGroup* group = new(std::nothrow) BParameterGroup(fWeb, name); if (group != NULL) fGroups->AddItem(group); return group; } int32 BParameterGroup::CountParameters() { return fControls->CountItems(); } BParameter* BParameterGroup::ParameterAt(int32 index) { return static_cast(fControls->ItemAt(index)); } int32 BParameterGroup::CountGroups() { return fGroups->CountItems(); } BParameterGroup* BParameterGroup::GroupAt(int32 index) { return static_cast(fGroups->ItemAt(index)); } bool BParameterGroup::IsFixedSize() const { return false; } type_code BParameterGroup::TypeCode() const { return B_MEDIA_PARAMETER_GROUP_TYPE; } ssize_t BParameterGroup::FlattenedSize() const { CALLED(); /* //--------BEGIN-CORE-BPARAMETERGROUP-STRUCT----------- ?? (0x03040507 OR 0x03040509 depending if the flags field is included or not???): 4 bytes (possible) Flags: 4 bytes Name String Length: 1 byte (??) Name String: 'Name String Length' bytes Param Count: 4 bytes //for each Param BEGIN Pointer: 4 bytes Parameter Type: 4 bytes Flattened Parameter Size: 4 bytes Flattened Parameter: 'Flattened Parameter Size' bytes //for each Param END Subgroup Count: 4 bytes //for each SubGroup BEGIN Pointer: 4 bytes MEDIA PARAMETER GROUP TYPE('BMCG' (opposite byte order in file)): 4 bytes Flattened Group Size: 4 bytes Flattened Group: 'Flattened Group Size' bytes //for each SubGroup END //---------END-CORE-BPARAMETERGROUP-STRUCT-------------- */ //13 guaranteed bytes, variable after that. ssize_t size = 13; if (fFlags != 0) size += sizeof(uint32); if (fName != NULL) size += min_c(strlen(fName), 255); int limit = fControls->CountItems(); for (int i = 0; i < limit; i++) { BParameter* parameter = static_cast(fControls->ItemAt(i)); if (parameter != NULL) { // overhead for each parameter flattened size += sizeof(BParameter*) + sizeof(BParameter::media_parameter_type) + sizeof(ssize_t) + parameter->FlattenedSize(); } } limit = fGroups->CountItems(); for (int i = 0; i < limit; i++) { BParameterGroup* group = static_cast(fGroups->ItemAt(i)); if (group != NULL) { // overhead for each group flattened size += sizeof(BParameterGroup*) + sizeof(type_code) + sizeof(ssize_t) + group->FlattenedSize(); } } return size; } status_t BParameterGroup::Flatten(void* buffer, ssize_t size) const { CALLED(); if (buffer == NULL) { ERROR("BParameterGroup::Flatten buffer is NULL\n"); return B_NO_INIT; } // NOTICE: It is important that this value is the size returned by // BParameterGroup::FlattenedSize, not by a descendent's override of this method. ssize_t actualSize = BParameterGroup::FlattenedSize(); if (size < actualSize) { ERROR("BParameterGroup::Flatten size to small\n"); return B_NO_MEMORY; } if (fFlags != 0) { write_to_buffer(&buffer, kBufferGroupMagic); write_to_buffer(&buffer, fFlags); } else write_to_buffer(&buffer, kBufferGroupMagicNoFlags); write_string_to_buffer(&buffer, fName); int32 count = fControls->CountItems(); write_to_buffer(&buffer, count); for (int32 i = 0; i < count; i++) { BParameter* parameter = static_cast(fControls->ItemAt(i)); if (parameter == NULL) { ERROR("BParameterGroup::Flatten(): NULL parameter\n"); continue; } write_to_buffer(&buffer, parameter); write_to_buffer(&buffer, parameter->Type()); // flatten parameter into this buffer ssize_t parameterSize = parameter->FlattenedSize(); write_to_buffer(&buffer, parameterSize); status_t status = parameter->Flatten(buffer, parameterSize); // we have only that much bytes left to write in this buffer if (status < B_OK) return status; skip_in_buffer(&buffer, parameterSize); } count = fGroups->CountItems(); write_to_buffer(&buffer, count); for (int32 i = 0; i < count; i++) { BParameterGroup* group = static_cast(fGroups->ItemAt(i)); if (group == NULL) { ERROR("BParameterGroup::Flatten(): NULL group\n"); continue; } write_to_buffer(&buffer, group); write_to_buffer(&buffer, group->TypeCode()); // flatten sub group into this buffer ssize_t groupSize = group->FlattenedSize(); write_to_buffer(&buffer, groupSize); status_t status = group->Flatten(buffer, groupSize); // we have only that much bytes left to write in this buffer if (status < B_OK) return status; skip_in_buffer(&buffer, groupSize); } return B_OK; } bool BParameterGroup::AllowsTypeCode(type_code code) const { return code == TypeCode(); } status_t BParameterGroup::Unflatten(type_code code, const void* buffer, ssize_t size) { CALLED(); if (!AllowsTypeCode(code)) { ERROR("BParameterGroup::Unflatten() wrong type code\n"); return B_BAD_TYPE; } if (buffer == NULL) { ERROR("BParameterGroup::Unflatten() buffer is NULL\n"); return B_NO_INIT; } // if the buffer is smaller than the size needed to read the // signature field, then there is a problem if (size < static_cast(sizeof(int32))) { ERROR("BParameterGroup::Unflatten() size to small\n"); return B_ERROR; } const void* bufferStart = buffer; // used to compute the rest length of the buffer when needed uint32 magic = read_from_buffer(&buffer); bool isSwapped = false; if (magic == B_SWAP_INT32(kBufferGroupMagic) || magic == B_SWAP_INT32(kBufferGroupMagicNoFlags)) { isSwapped = true; magic = B_SWAP_INT32(magic); } if (magic == kBufferGroupMagic) fFlags = read_from_buffer_swap32(&buffer, isSwapped); else if (magic == kBufferGroupMagicNoFlags) fFlags = 0; else return B_BAD_TYPE; if (read_string_from_buffer(&buffer, &fName, size - (ssize_t)((uint8*)buffer - (uint8*)bufferStart)) < B_OK) return B_BAD_VALUE; // Clear all existing parameters/subgroups for (int32 i = 0; i < fControls->CountItems(); i++) { delete static_cast(fControls->ItemAt(i)); } fControls->MakeEmpty(); for (int32 i = 0; i < fGroups->CountItems(); i++) { delete static_cast(fGroups->ItemAt(i)); } fGroups->MakeEmpty(); // unflatten parameter list int32 count = read_from_buffer_swap32(&buffer, isSwapped); if (count < 0 || count * kAdditionalParameterSize > size_left(size, bufferStart, buffer)) return B_BAD_VALUE; for (int32 i = 0; i < count; i++) { // make sure we can read as many bytes if (size_left(size, bufferStart, buffer) < (ssize_t)( sizeof(BParameter*) + sizeof(BParameter::media_parameter_type) + sizeof(ssize_t))) { return B_BAD_VALUE; } BParameter* oldPointer = read_pointer_from_buffer_swap( &buffer, isSwapped); BParameter::media_parameter_type mediaType = read_from_buffer_swap32(&buffer, isSwapped); ssize_t parameterSize = read_pointer_from_buffer_swap(&buffer, isSwapped); if (parameterSize > size_left(size, bufferStart, buffer)) return B_BAD_VALUE; BParameter* parameter = MakeControl(mediaType); if (parameter == NULL) { ERROR("BParameterGroup::Unflatten(): MakeControl() failed\n"); return B_ERROR; } status_t status = parameter->Unflatten(parameter->TypeCode(), buffer, parameterSize); if (status < B_OK) { ERROR("BParameterGroup::Unflatten(): parameter->Unflatten() failed\n"); delete parameter; return status; } skip_in_buffer(&buffer, parameterSize); // add the item to the list parameter->fGroup = this; parameter->fWeb = fWeb; fControls->AddItem(parameter); // add it's old pointer value to the RefFix list kept by the owner web if (fWeb != NULL) fWeb->AddRefFix(oldPointer, parameter); } // unflatten sub groups count = read_from_buffer_swap32(&buffer, isSwapped); if (count < 0 || count * kAdditionalParameterGroupSize > size_left(size, bufferStart, buffer)) return B_BAD_VALUE; for (int32 i = 0; i < count; i++) { // make sure we can read as many bytes if (size_left(size, bufferStart, buffer) < (ssize_t)( sizeof(BParameterGroup*) + sizeof(type_code) + sizeof(ssize_t))) { return B_BAD_VALUE; } BParameterGroup* oldPointer = read_pointer_from_buffer_swap< BParameterGroup*>(&buffer, isSwapped); type_code type = read_from_buffer_swap32(&buffer, isSwapped); ssize_t groupSize = read_pointer_from_buffer_swap(&buffer, isSwapped); if (groupSize > size_left(size, bufferStart, buffer)) return B_BAD_VALUE; BParameterGroup* group = new BParameterGroup(fWeb, "sub-unnamed"); if (group == NULL) { ERROR("BParameterGroup::Unflatten(): MakeGroup() failed\n"); return B_ERROR; } status_t status = group->Unflatten(type, buffer, groupSize); if (status != B_OK) { ERROR("BParameterGroup::Unflatten(): group->Unflatten() failed\n"); delete group; return status; } skip_in_buffer(&buffer, groupSize); fGroups->AddItem(group); // add it's old pointer value to the RefFix list kept by the owner web if (fWeb != NULL) fWeb->AddRefFix(oldPointer, group); } return B_OK; } /*! Creates an uninitialized parameter of the specified type. Unlike the BParameterGroup::MakeXXXParameter() type of methods, this method does not add the parameter to this group automatically. */ BParameter* BParameterGroup::MakeControl(int32 type) { CALLED(); switch (type) { case BParameter::B_NULL_PARAMETER: return new BNullParameter(-1, B_MEDIA_NO_TYPE, NULL, NULL, NULL); case BParameter::B_DISCRETE_PARAMETER: return new BDiscreteParameter(-1, B_MEDIA_NO_TYPE, NULL, NULL, NULL); case BParameter::B_CONTINUOUS_PARAMETER: return new BContinuousParameter(-1, B_MEDIA_NO_TYPE, NULL, NULL, NULL, NULL, 0, 0, 0); case BParameter::B_TEXT_PARAMETER: return new BTextParameter(-1, B_MEDIA_NO_TYPE, NULL, NULL, NULL, 0); default: ERROR("BParameterGroup::MakeControl unknown type %" B_PRId32 "\n", type); return NULL; } } // #pragma mark - BParameter BParameter::media_parameter_type BParameter::Type() const { return fType; } BParameterWeb* BParameter::Web() const { return fWeb; } BParameterGroup* BParameter::Group() const { return fGroup; } const char* BParameter::Name() const { return fName; } const char* BParameter::Kind() const { return fKind; } const char* BParameter::Unit() const { return fUnit; } int32 BParameter::ID() const { return fID; } void BParameter::SetFlags(uint32 flags) { fFlags = flags; } uint32 BParameter::Flags() const { return fFlags; } status_t BParameter::GetValue(void* buffer, size_t* _size, bigtime_t* _when) { CALLED(); if (buffer == NULL || _size == NULL) return B_BAD_VALUE; size_t size = *_size; if (size <= 0) return B_NO_MEMORY; if (fWeb == NULL) { ERROR("BParameter::GetValue: no parent BParameterWeb\n"); return B_NO_INIT; } media_node node = fWeb->Node(); if (IS_INVALID_NODE(node)) { ERROR("BParameter::GetValue: the parent BParameterWeb is not assigned to a BMediaNode\n"); return B_NO_INIT; } controllable_get_parameter_data_request request; controllable_get_parameter_data_reply reply; area_id area; void* data; if (size > MAX_PARAMETER_DATA) { // create an area if large data needs to be transfered area = create_area("get parameter data", &data, B_ANY_ADDRESS, ROUND_UP_TO_PAGE(size), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (area < B_OK) { ERROR("BParameter::GetValue can't create area of %ld bytes\n", size); return B_NO_MEMORY; } } else { area = -1; data = reply.raw_data; } request.parameter_id = fID; request.request_size = size; request.area = area; status_t status = QueryPort(node.port, CONTROLLABLE_GET_PARAMETER_DATA, &request, sizeof(request), &reply, sizeof(reply)); if (status == B_OK) { // we don't want to copy more than the buffer provides if (reply.size < size) size = reply.size; memcpy(buffer, data, size); // store reported values *_size = reply.size; if (_when != NULL) *_when = reply.last_change; } else { ERROR("BParameter::GetValue parameter '%s' querying node %d, " "port %d failed: %s\n", fName, (int)node.node, (int)node.port, strerror(status)); } if (area >= B_OK) delete_area(area); return status; } status_t BParameter::SetValue(const void* buffer, size_t size, bigtime_t when) { CALLED(); if (buffer == 0) return B_BAD_VALUE; if (size <= 0) return B_NO_MEMORY; if (fWeb == 0) { ERROR("BParameter::SetValue: no parent BParameterWeb\n"); return B_NO_INIT; } media_node node = fWeb->Node(); if (IS_INVALID_NODE(node)) { ERROR("BParameter::SetValue: the parent BParameterWeb is not assigned " "to a BMediaNode\n"); return B_NO_INIT; } controllable_set_parameter_data_request request; controllable_set_parameter_data_reply reply; area_id area; void* data; if (size > MAX_PARAMETER_DATA) { // create an area if large data needs to be transfered area = create_area("set parameter data", &data, B_ANY_ADDRESS, ROUND_UP_TO_PAGE(size), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (area < B_OK) { ERROR("BParameter::SetValue can't create area of %ld bytes\n", size); return B_NO_MEMORY; } } else { area = -1; data = request.raw_data; } memcpy(data, buffer, size); request.parameter_id = fID; request.when = when; request.area = area; request.size = size; status_t status = QueryPort(node.port, CONTROLLABLE_SET_PARAMETER_DATA, &request, sizeof(request), &reply, sizeof(reply)); if (status != B_OK) { ERROR("BParameter::SetValue querying node failed: %s\n", strerror(status)); } if (area != -1) delete_area(area); return status; } int32 BParameter::CountChannels() { return fChannels; } void BParameter::SetChannelCount(int32 count) { fChannels = count; } media_type BParameter::MediaType() { return fMediaType; } void BParameter::SetMediaType(media_type type) { fMediaType = type; } int32 BParameter::CountInputs() { return fInputs->CountItems(); } BParameter* BParameter::InputAt(int32 index) { return static_cast(fInputs->ItemAt(index)); } void BParameter::AddInput(BParameter* input) { CALLED(); // BeBook has this method returning a status value, // but it should be updated if (input == NULL) return; if (fInputs->HasItem(input)) { // if already in input list, don't duplicate. return; } fInputs->AddItem(input); input->AddOutput(this); } int32 BParameter::CountOutputs() { return fOutputs->CountItems(); } BParameter* BParameter::OutputAt(int32 index) { return static_cast(fOutputs->ItemAt(index)); } void BParameter::AddOutput(BParameter* output) { CALLED(); // BeBook has this method returning a status value, // but it should be updated if (output == NULL) return; if (fOutputs->HasItem(output)) { // if already in output list, don't duplicate. return; } fOutputs->AddItem(output); output->AddInput(this); } bool BParameter::IsFixedSize() const { return false; } type_code BParameter::TypeCode() const { return B_MEDIA_PARAMETER_TYPE; } ssize_t BParameter::FlattenedSize() const { CALLED(); /* ?? (0x02040607): 4 bytes BParameter Struct Size (in bytes): 4 bytes ID: 4 bytes Name String Length: 1 byte (??) Name String: 'Name String Length' bytes Kind String Length: 1 byte (??) Kind String: 'Kind String Length' bytes Unit String Length: 1 byte (??) Unit String: 'Unit String Length' bytes Inputs Count: 4 bytes Inputs (pointers): ('Inputs Count')*4 bytes Outputs Count: 4 bytes Outputs (pointers): ('Outputs Count')*4 bytes Media Type: 4 bytes ChannelCount: 4 bytes Flags: 4 bytes */ //35 bytes are guaranteed, after that, add the variable length parts. ssize_t size = kAdditionalParameterSize; if (fName != NULL) size += strlen(fName); if (fKind != NULL) size += strlen(fKind); if (fUnit != NULL) size += strlen(fUnit); size += fInputs->CountItems() * sizeof(BParameter*); size += fOutputs->CountItems() * sizeof(BParameter*); return size; } status_t BParameter::Flatten(void* buffer, ssize_t size) const { CALLED(); if (buffer == NULL) { ERROR("BParameter::Flatten buffer is NULL\n"); return B_NO_INIT; } // NOTICE: It is important that this value is the size returned by // BParameter::FlattenedSize(), not by a descendent's override of this method. ssize_t actualSize = BParameter::FlattenedSize(); if (size < actualSize) { ERROR("BParameter::Flatten(): size too small\n"); return B_NO_MEMORY; } write_to_buffer(&buffer, kParameterMagic); write_to_buffer(&buffer, actualSize); write_to_buffer(&buffer, fID); write_string_to_buffer(&buffer, fName); write_string_to_buffer(&buffer, fKind); write_string_to_buffer(&buffer, fUnit); // flatten and write the list of inputs ssize_t count = fInputs->CountItems(); write_to_buffer(&buffer, count); if (count > 0) { memcpy(buffer, fInputs->Items(), sizeof(BParameter*) * count); skip_in_buffer(&buffer, sizeof(BParameter*) * count); } // flatten and write the list of outputs count = fOutputs->CountItems(); write_to_buffer(&buffer, count); if (count > 0) { memcpy(buffer, fOutputs->Items(), sizeof(BParameter*) * count); skip_in_buffer(&buffer, sizeof(BParameter*) * count); } write_to_buffer(&buffer, fMediaType); write_to_buffer(&buffer, fChannels); write_to_buffer(&buffer, fFlags); return B_OK; } bool BParameter::AllowsTypeCode(type_code code) const { return code == TypeCode(); } status_t BParameter::Unflatten(type_code code, const void* buffer, ssize_t size) { CALLED(); if (!AllowsTypeCode(code)) { ERROR("BParameter::Unflatten(): wrong type code\n"); return B_BAD_TYPE; } if (buffer == NULL) { ERROR("BParameter::Unflatten(): buffer is NULL\n"); return B_NO_INIT; } // if the buffer is smaller than the size needed to read the // signature and struct size fields, then there is a problem if (size < static_cast(sizeof(int32) + sizeof(ssize_t))) { ERROR("BParameter::Unflatten() size too small\n"); return B_BAD_VALUE; } const void* bufferStart = buffer; // check magic uint32 magic = read_from_buffer(&buffer); if (magic == B_SWAP_INT32(kParameterMagic)) fSwapDetected = true; else if (magic == kParameterMagic) fSwapDetected = false; else { ERROR("BParameter::Unflatten(): bad magic\n"); return B_BAD_TYPE; } ssize_t parameterSize = read_pointer_from_buffer_swap(&buffer, fSwapDetected); if (parameterSize > size) { ERROR("BParameter::Unflatten(): buffer too small (%ld > %ld)\n", parameterSize, size); return B_BAD_VALUE; } // if the struct doesn't meet the minimum size for // a flattened BParameter, then return an error. // MinFlattenedParamSize = // ID (4 bytes) // Name String Length (1 byte) // Kind String Length (1 byte) // Unit String Length (1 byte) // Inputs Count (4 bytes) // Outputs Count (4 bytes) // Media Type (4 bytes) // Channel Count (4 bytes) // Flags (4 bytes) // TOTAL: 27 bytes const ssize_t kMinFlattenedParamSize = 15 + 3 * sizeof(ssize_t); if (parameterSize < kMinFlattenedParamSize) { ERROR("BParameter::Unflatten out of memory (2)\n"); return B_ERROR; } fID = read_from_buffer_swap32(&buffer, fSwapDetected); if (read_string_from_buffer(&buffer, &fName, size_left(size, bufferStart, buffer)) < B_OK || read_string_from_buffer(&buffer, &fKind, size_left(size, bufferStart, buffer)) < B_OK || read_string_from_buffer(&buffer, &fUnit, size_left(size, bufferStart, buffer)) < B_OK) return B_NO_MEMORY; // read the list of inputs // it will directly add the pointers in the flattened message to the list; // these will be fixed to point to the real inputs/outputs later in FixRefs() ssize_t count = read_pointer_from_buffer_swap(&buffer, fSwapDetected); fInputs->MakeEmpty(); for (ssize_t i = 0; i < count; i++) { fInputs->AddItem(read_pointer_from_buffer_swap( &buffer, fSwapDetected)); } // read the list of outputs count = read_pointer_from_buffer_swap(&buffer, fSwapDetected); fOutputs->MakeEmpty(); for (ssize_t i = 0; i < count; i++) { fOutputs->AddItem(read_pointer_from_buffer_swap( &buffer, fSwapDetected)); } fMediaType = read_from_buffer_swap32(&buffer, fSwapDetected); fChannels = read_from_buffer_swap32(&buffer, fSwapDetected); fFlags = read_from_buffer_swap32(&buffer, fSwapDetected); return B_OK; } BParameter::BParameter(int32 id, media_type mediaType, media_parameter_type type, BParameterWeb* web, const char* name, const char* kind, const char* unit) : fID(id), fType(type), fWeb(web), fGroup(NULL), fSwapDetected(true), fMediaType(mediaType), fChannels(1), fFlags(0) { CALLED(); fName = strndup(name, 255); fKind = strndup(kind, 255); fUnit = strndup(unit, 255); // create empty input/output lists fInputs = new BList(); fOutputs = new BList(); } BParameter::~BParameter() { CALLED(); // don't worry about the fWeb/fGroup properties, you don't need // to remove yourself from a web/group since the only way in which // a parameter is destroyed is when the owner web/group destroys it free(fName); free(fKind); free(fUnit); delete fInputs; delete fOutputs; } /*! Replaces references to items in the old list with the corresponding items in the updated list. The references are replaced in the input and output lists. This is called by BParameterWeb::Unflatten(). */ void BParameter::FixRefs(BList& old, BList& updated) { CALLED(); // update inputs void** items = static_cast(fInputs->Items()); int32 count = fInputs->CountItems(); for (int32 i = 0; i < count; i++) { int32 index = old.IndexOf(items[i]); if (index >= 0) items[i] = updated.ItemAt(index); else { ERROR("BParameter::FixRefs(): No mapping found for input"); items[i] = NULL; } } // remove all NULL inputs (those which couldn't be mapped) for (int32 i = count; i-- > 0;) { if (items[i] == NULL) fInputs->RemoveItem(i); } // update outputs items = static_cast(fOutputs->Items()); count = fOutputs->CountItems(); for (int32 i = 0; i < count; i++) { int32 index = old.IndexOf(items[i]); if (index >= 0) items[i] = updated.ItemAt(index); else { ERROR("BParameter::FixRefs(): No mapping found for output"); items[i] = NULL; } } // remove all NULL outputs (those which couldn't be mapped) for (int32 i = count; i-- > 0;) { if (items[i] == NULL) fOutputs->RemoveItem(i); } } // #pragma mark - public BContinuousParameter type_code BContinuousParameter::ValueType() { return B_FLOAT_TYPE; } float BContinuousParameter::MinValue() { return fMinimum; } float BContinuousParameter::MaxValue() { return fMaximum; } float BContinuousParameter::ValueStep() { return fStepping; } void BContinuousParameter::SetResponse(int resp, float factor, float offset) { fResponse = static_cast(resp); fFactor = factor; fOffset = offset; } void BContinuousParameter::GetResponse(int* _resp, float* _factor, float* _offset) { if (_resp != NULL) *_resp = fResponse; if (_factor != NULL) *_factor = fFactor; if (_offset != NULL) *_offset = fOffset; } ssize_t BContinuousParameter::FlattenedSize() const { CALLED(); // only adds a fixed amount of bytes return BParameter::FlattenedSize() + kAdditionalContinuousParameterSize; } status_t BContinuousParameter::Flatten(void* buffer, ssize_t size) const { CALLED(); if (buffer == NULL) { ERROR("BContinuousParameter::Flatten(): buffer is NULL\n"); return B_NO_INIT; } ssize_t parameterSize = BParameter::FlattenedSize(); if (size < (parameterSize + kAdditionalContinuousParameterSize)) { ERROR("BContinuousParameter::Flatten(): size to small\n"); return B_NO_MEMORY; } status_t status = BParameter::Flatten(buffer, size); if (status != B_OK) { ERROR("BContinuousParameter::Flatten(): BParameter::Flatten() failed\n"); return status; } // add our data to the general flattened BParameter skip_in_buffer(&buffer, parameterSize); write_to_buffer(&buffer, fMinimum); write_to_buffer(&buffer, fMaximum); write_to_buffer(&buffer, fStepping); write_to_buffer(&buffer, fResponse); write_to_buffer(&buffer, fFactor); write_to_buffer(&buffer, fOffset); return B_OK; } status_t BContinuousParameter::Unflatten(type_code code, const void* buffer, ssize_t size) { CALLED(); // we try to check if the buffer size is long enough to hold an object // as early as possible. if (!AllowsTypeCode(code)) { ERROR("BContinuousParameter::Unflatten wrong type code\n"); return B_BAD_TYPE; } if (buffer == NULL) { ERROR("BContinuousParameter::Unflatten buffer is NULL\n"); return B_NO_INIT; } // if the buffer is smaller than the size needed to read the // signature and struct size fields, then there is a problem if (size < static_cast(sizeof(int32) + sizeof(ssize_t))) { ERROR("BContinuousParameter::Unflatten size too small\n"); return B_ERROR; } status_t status = BParameter::Unflatten(code, buffer, size); if (status != B_OK) { ERROR("BContinuousParameter::Unflatten(): BParameter::Unflatten " "failed: %s\n", strerror(status)); return status; } ssize_t parameterSize = BParameter::FlattenedSize(); skip_in_buffer(&buffer, parameterSize); if (size < (parameterSize + kAdditionalContinuousParameterSize)) { ERROR("BContinuousParameter::Unflatten(): buffer too small\n"); return B_BAD_VALUE; } fMinimum = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); fMaximum = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); fStepping = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); fResponse = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); fFactor = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); fOffset = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); return B_OK; } BContinuousParameter::BContinuousParameter(int32 id, media_type mediaType, BParameterWeb* web, const char* name, const char* kind, const char* unit, float minimum, float maximum, float stepping) : BParameter(id, mediaType, B_CONTINUOUS_PARAMETER, web, name, kind, unit), fMinimum(minimum), fMaximum(maximum), fStepping(stepping), fResponse(B_LINEAR), fFactor(1.0), fOffset(0.0) { CALLED(); } BContinuousParameter::~BContinuousParameter() { CALLED(); } // #pragma mark - public BDiscreteParameter type_code BDiscreteParameter::ValueType() { return B_INT32_TYPE; } int32 BDiscreteParameter::CountItems() { return fValues->CountItems(); } const char* BDiscreteParameter::ItemNameAt(int32 index) { return reinterpret_cast(fSelections->ItemAt(index)); } int32 BDiscreteParameter::ItemValueAt(int32 index) { int32* item = static_cast(fValues->ItemAt(index)); if (item == NULL) return 0; return *item; } status_t BDiscreteParameter::AddItem(int32 value, const char* name) { CALLED(); int32* valueCopy = new(std::nothrow) int32(value); if (valueCopy == NULL) return B_NO_MEMORY; char* nameCopy = strndup(name, 255); if (name != NULL && nameCopy == NULL) { delete valueCopy; return B_NO_MEMORY; } if (!fValues->AddItem(valueCopy)) goto err; if (!fSelections->AddItem(nameCopy)) { fValues->RemoveItem(valueCopy); goto err; } return B_OK; err: free(nameCopy); delete valueCopy; return B_NO_MEMORY; } status_t BDiscreteParameter::MakeItemsFromInputs() { CALLED(); int32 count = fInputs->CountItems(); for (int32 i = 0; i < count; i++) { BParameter* parameter = static_cast(fInputs->ItemAt(i)); AddItem(i, parameter->Name()); } return B_OK; } status_t BDiscreteParameter::MakeItemsFromOutputs() { CALLED(); int32 count = fOutputs->CountItems(); for (int32 i = 0; i < count; i++) { BParameter* parameter = static_cast(fOutputs->ItemAt(i)); AddItem(i, parameter->Name()); } return B_OK; } void BDiscreteParameter::MakeEmpty() { CALLED(); for (int32 i = fValues->CountItems(); i-- > 0;) { delete static_cast(fValues->ItemAt(i)); } fValues->MakeEmpty(); for (int32 i = fSelections->CountItems(); i-- > 0;) { free(static_cast(fSelections->ItemAt(i))); } fSelections->MakeEmpty(); } ssize_t BDiscreteParameter::FlattenedSize() const { CALLED(); ssize_t size = BParameter::FlattenedSize() + kAdditionalDiscreteParameterSize; int32 count = fValues->CountItems(); for (int32 i = 0; i < count; i++) { char* selection = static_cast(fSelections->ItemAt(i)); if (selection != NULL) size += min_c(strlen(selection), 255); size += 5; // string length + value } return size; } status_t BDiscreteParameter::Flatten(void* buffer, ssize_t size) const { CALLED(); if (buffer == NULL) { ERROR("BDiscreteParameter::Flatten(): buffer is NULL\n"); return B_NO_INIT; } ssize_t parameterSize = BParameter::FlattenedSize(); if (size < FlattenedSize()) { ERROR("BDiscreteParameter::Flatten(): size too small\n"); return B_NO_MEMORY; } status_t status = BParameter::Flatten(buffer, size); if (status != B_OK) { ERROR("BDiscreteParameter::Flatten(): BParameter::Flatten failed\n"); return status; } skip_in_buffer(&buffer, parameterSize); int32 count = fValues->CountItems(); write_to_buffer(&buffer, count); // write out all value/name pairs for (int32 i = 0; i < count; i++) { const char* selection = static_cast(fSelections->ItemAt(i)); const int32* value = static_cast(fValues->ItemAt(i)); write_string_to_buffer(&buffer, selection); write_to_buffer(&buffer, value ? *value : 0); } return B_OK; } status_t BDiscreteParameter::Unflatten(type_code code, const void* buffer, ssize_t size) { CALLED(); if (!AllowsTypeCode(code)) { ERROR("BDiscreteParameter::Unflatten(): bad type code\n"); return B_BAD_TYPE; } if (buffer == NULL) { ERROR("BDiscreteParameter::Unflatten(): buffer is NULL\n"); return B_NO_INIT; } // if the buffer is smaller than the size needed to read the // signature and struct size fields, then there is a problem if (size < static_cast(sizeof(int32) + sizeof(ssize_t))) { ERROR("BDiscreteParameter::Unflatten(): size too small\n"); return B_ERROR; } const void* bufferStart = buffer; status_t status = BParameter::Unflatten(code, buffer, size); if (status != B_OK) { ERROR("BDiscreteParameter::Unflatten(): BParameter::Unflatten failed\n"); return status; } ssize_t parameterSize = BParameter::FlattenedSize(); skip_in_buffer(&buffer, parameterSize); if (size < (parameterSize + kAdditionalDiscreteParameterSize)) { ERROR("BDiscreteParameter::Unflatten(): buffer too small\n"); return B_BAD_VALUE; } int32 count = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); // clear any existing name/value pairs MakeEmpty(); for (int32 i = 0; i < count; i++) { char* name = NULL; if (read_string_from_buffer(&buffer, &name, size_left(size, bufferStart, buffer)) < B_OK) return B_BAD_DATA; if (size_left(size, bufferStart, buffer) < (int)sizeof(int32)) { free(name); return B_BAD_DATA; } int32 value = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); AddItem(value, name); free(name); } return B_OK; } BDiscreteParameter::BDiscreteParameter(int32 id, media_type mediaType, BParameterWeb* web, const char* name, const char* kind) : BParameter(id, mediaType, B_DISCRETE_PARAMETER, web, name, kind, NULL) { CALLED(); fSelections = new BList(); fValues = new BList(); } BDiscreteParameter::~BDiscreteParameter() { CALLED(); MakeEmpty(); delete fSelections; delete fValues; } // #pragma mark - public BTextParameter size_t BTextParameter::MaxBytes() const { return fMaxBytes; } type_code BTextParameter::ValueType() { return B_FLOAT_TYPE; } ssize_t BTextParameter::FlattenedSize() const { return BParameter::FlattenedSize() + sizeof(fMaxBytes); } status_t BTextParameter::Flatten(void* buffer, ssize_t size) const { if (buffer == NULL) { ERROR("BTextParameter::Flatten(): buffer is NULL\n"); return B_NO_INIT; } ssize_t parameterSize = BParameter::FlattenedSize(); if (size < static_cast(parameterSize + sizeof(fMaxBytes))) { ERROR("BContinuousParameter::Flatten(): size to small\n"); return B_NO_MEMORY; } status_t status = BParameter::Flatten(buffer, size); if (status != B_OK) { ERROR("BTextParameter::Flatten(): BParameter::Flatten() failed\n"); return status; } // add our data to the general flattened BParameter skip_in_buffer(&buffer, parameterSize); write_to_buffer(&buffer, fMaxBytes); return B_OK; } status_t BTextParameter::Unflatten(type_code code, const void* buffer, ssize_t size) { // we try to check if the buffer size is long enough to hold an object // as early as possible. if (!AllowsTypeCode(code)) { ERROR("BTextParameter::Unflatten wrong type code\n"); return B_BAD_TYPE; } if (buffer == NULL) { ERROR("BTextParameter::Unflatten buffer is NULL\n"); return B_NO_INIT; } if (size < static_cast(sizeof(fMaxBytes))) { ERROR("BTextParameter::Unflatten size too small\n"); return B_ERROR; } status_t status = BParameter::Unflatten(code, buffer, size); if (status != B_OK) { ERROR("BTextParameter::Unflatten(): BParameter::Unflatten failed\n"); return status; } ssize_t parameterSize = BParameter::FlattenedSize(); skip_in_buffer(&buffer, parameterSize); if (size < static_cast(parameterSize + sizeof(fMaxBytes))) { ERROR("BTextParameter::Unflatten(): buffer too small\n"); return B_BAD_VALUE; } fMaxBytes = read_from_buffer_swap32(&buffer, SwapOnUnflatten()); return B_OK; } BTextParameter::BTextParameter(int32 id, media_type mediaType, BParameterWeb* web, const char* name, const char* kind, size_t maxBytes) : BParameter(id, mediaType, B_TEXT_PARAMETER, web, name, kind, NULL) { fMaxBytes = maxBytes; } BTextParameter::~BTextParameter() { } // #pragma mark - public BNullParameter type_code BNullParameter::ValueType() { // NULL parameters have no value type return 0; } ssize_t BNullParameter::FlattenedSize() const { return BParameter::FlattenedSize(); } status_t BNullParameter::Flatten(void* buffer, ssize_t size) const { return BParameter::Flatten(buffer, size); } status_t BNullParameter::Unflatten(type_code code, const void* buffer, ssize_t size) { return BParameter::Unflatten(code, buffer, size); } BNullParameter::BNullParameter(int32 id, media_type mediaType, BParameterWeb* web, const char* name, const char* kind) : BParameter(id, mediaType, B_NULL_PARAMETER, web, name, kind, NULL) { } BNullParameter::~BNullParameter() { } // #pragma mark - reserved functions status_t BParameterWeb::_Reserved_ControlWeb_0(void *) { return B_ERROR; } status_t BParameterWeb::_Reserved_ControlWeb_1(void *) { return B_ERROR; } status_t BParameterWeb::_Reserved_ControlWeb_2(void *) { return B_ERROR; } status_t BParameterWeb::_Reserved_ControlWeb_3(void *) { return B_ERROR; } status_t BParameterWeb::_Reserved_ControlWeb_4(void *) { return B_ERROR; } status_t BParameterWeb::_Reserved_ControlWeb_5(void *) { return B_ERROR; } status_t BParameterWeb::_Reserved_ControlWeb_6(void *) { return B_ERROR; } status_t BParameterWeb::_Reserved_ControlWeb_7(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_0(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_1(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_2(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_3(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_4(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_5(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_6(void *) { return B_ERROR; } status_t BParameterGroup::_Reserved_ControlGroup_7(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_0(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_1(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_2(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_3(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_4(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_5(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_6(void *) { return B_ERROR; } status_t BParameter::_Reserved_Control_7(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_0(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_1(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_2(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_3(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_4(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_5(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_6(void *) { return B_ERROR; } status_t BContinuousParameter::_Reserved_ContinuousParameter_7(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_0(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_1(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_2(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_3(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_4(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_5(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_6(void *) { return B_ERROR; } status_t BDiscreteParameter::_Reserved_DiscreteParameter_7(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_0(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_1(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_2(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_3(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_4(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_5(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_6(void *) { return B_ERROR; } status_t BNullParameter::_Reserved_NullParameter_7(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_0(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_1(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_2(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_3(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_4(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_5(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_6(void *) { return B_ERROR; } status_t BTextParameter::_Reserved_TextParameter_7(void *) { return B_ERROR; }