void FrameFactory::updateGenre(TextIdentificationFrame *frame) const { StringList fields = frame->fieldList(); StringList newfields; for(StringList::Iterator it = fields.begin(); it != fields.end(); ++it) { String s = *it; int end = s.find(")"); if(s.startsWith("(") && end > 0) { // "(12)Genre" String text = s.substr(end + 1); bool ok; int number = s.substr(1, end - 1).toInt(&ok); if(ok && number >= 0 && number <= 255 && !(ID3v1::genre(number) == text)) newfields.append(s.substr(1, end - 1)); if(!text.isEmpty()) newfields.append(text); } else { // "Genre" or "12" newfields.append(s); } } if(newfields.isEmpty()) fields.append(String::null); frame->setText(newfields); }
void FrameFactory::updateGenre(TextIdentificationFrame *frame) const { StringList fields; String s = frame->toString(); while(s.startsWith("(")) { int closing = s.find(")"); if(closing < 0) break; fields.append(s.substr(1, closing - 1)); s = s.substr(closing + 1); } if(!s.isEmpty()) fields.append(s); if(fields.isEmpty()) fields.append(String::null); frame->setText(fields); }
StringList ID3v1::genreList() { static StringList l; if(l.isEmpty()) { for(int i = 0; i < genresSize; i++) l.append(genres[i]); } return l; }
void UserTextIdentificationFrame::setDescription(const String &s) { StringList l = fieldList(); if(l.isEmpty()) l.append(s); else l[0] = s; TextIdentificationFrame::setText(l); }
StringList UserTextIdentificationFrame::fieldList() const { StringList l = TextIdentificationFrame::fieldList(); if(!l.isEmpty()) { StringList::Iterator it = l.begin(); l.erase(it); } return l; }
PropertyMap MP4::Tag::setProperties(const PropertyMap &props) { static Map<String, String> reverseKeyMap; if(reverseKeyMap.isEmpty()) { int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); for(int i = 0; i < numKeys; i++) { reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0]; } } PropertyMap origProps = properties(); for(PropertyMap::ConstIterator it = origProps.begin(); it != origProps.end(); ++it) { if(!props.contains(it->first) || props[it->first].isEmpty()) { d->items.erase(reverseKeyMap[it->first]); } } PropertyMap ignoredProps; for(PropertyMap::ConstIterator it = props.begin(); it != props.end(); ++it) { if(reverseKeyMap.contains(it->first)) { String name = reverseKeyMap[it->first]; if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) { StringList parts = StringList::split(it->second.front(), "/"); if(!parts.isEmpty()) { int first = parts[0].toInt(); int second = 0; if(parts.size() > 1) { second = parts[1].toInt(); } d->items[name] = MP4::Item(first, second); } } else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || it->first == "MOVEMENTCOUNT") && !it->second.isEmpty()) { int value = it->second.front().toInt(); d->items[name] = MP4::Item(value); } else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT") && !it->second.isEmpty()) { bool value = (it->second.front().toInt() != 0); d->items[name] = MP4::Item(value); } else { d->items[name] = it->second; } } else { ignoredProps.insert(it->first, it->second); } } return ignoredProps; }
// copy attrs into stringlist, returns true if list was modified // if append is false, list is cleared first. // if check_exist is true, items are only added if the are not already in the list. comparison is case-insensitive. bool initStringListFromAttrs(StringList & list, bool append, const classad::References & attrs, bool check_exist /*=false*/) { bool modified = false; if ( ! append) { if ( ! list.isEmpty()) { modified = true; list.clearAll(); } check_exist = false; // no need to do this if we cleared the list } for (classad::References::const_iterator it = attrs.begin(); it != attrs.end(); ++it) { if (check_exist && list.contains_anycase(it->c_str())) { continue; } list.append(it->c_str()); modified = true; } return modified; }
void Sinful::parseV1String() { std::vector< SourceRoute > v; if(! getSourceRoutes( v, & m_host, & m_port ) ) { m_valid = false; return; } // // To convert a list of source routes back into an original Sinful's // data structures, do the following: // // (1) Extract the spid from each route; they must all be the same. // Set the spid. // (2) Extract the alias from each route; they must all be the same. // Set the alias. // (3) Extract the private network name from each route; they must // all be the same. Set the private network name. // (4) Check all routes for ccbid. Each route with a ccbid goes // into the ccb contact list. // (5) All routes without a ccbid must be "Internet" addresses or // private network addresses. The former go into addrs (and // host is set to addrs[0]). Ignore all of the latter that // have an address in addrs (because those came from CCB). The // remaining address must be the private network address. // // Determine the shared port ID, if any. If any route has a // shared port ID, all must have one, and it must be the same. const std::string & sharedPortID = v[0].getSharedPortID(); if(! sharedPortID.empty() ) { setSharedPortID( v[0].getSharedPortID().c_str() ); for( unsigned i = 0; i < v.size(); ++i ) { if( v[i].getSharedPortID() != sharedPortID ) { m_valid = false; return; } } } // Determine the alias, if any. If more than one route has an alias, // each alias must be the same. std::string alias; for( unsigned i = 0; i < v.size(); ++i ) { if(! v[i].getAlias().empty()) { if(! alias.empty()) { if( alias != v[i].getAlias() ) { m_valid = false; return; } } else { alias = v[i].getAlias(); } } } if(! alias.empty() ) { setAlias( alias.c_str() ); } // Determine the private network name, if any. If more than one route // has a private network name, each private network name must be the same. std::string privateNetworkName; for( unsigned i = 0; i < v.size(); ++i ) { if( v[i].getNetworkName() != PUBLIC_NETWORK_NAME ) { if(! privateNetworkName.empty()) { if( v[i].getNetworkName() != privateNetworkName ) { m_valid = false; return; } } else { privateNetworkName = v[i].getNetworkName(); } } } if(! privateNetworkName.empty() ) { setPrivateNetworkName( privateNetworkName.c_str() ); } // // Determine the CCB contact string, if any. // // Each group of routes which shared a broker index must be converted // back into the single original Sinful from which it sprang; that // Sinful can than be added to the ccbList with its CCB ID. // StringList ccbList; std::map< unsigned, std::string > brokerCCBIDs; std::map< unsigned, std::vector< SourceRoute > > brokers; for( unsigned i = 0; i < v.size(); ++i ) { if( v[i].getCCBID().empty() ) { continue; } SourceRoute sr = v[i]; sr.setSharedPortID( sr.getCCBSharedPortID() ); sr.setCCBSharedPortID( "" ); sr.setCCBID( "" ); unsigned brokerIndex = sr.getBrokerIndex(); brokers[brokerIndex].push_back( sr ); brokerCCBIDs[brokerIndex] = v[i].getCCBID(); dprintf( D_ALWAYS, "broker %u = %s\n", brokerIndex, sr.serialize().c_str() ); } for( unsigned i = 0; i < brokers.size(); ++i ) { std::string brokerV1String = "{"; brokerV1String += brokers[i][0].serialize(); for( unsigned j = 0; j < brokers[i].size(); ++j ) { brokerV1String += ", "; brokerV1String += brokers[i][j].serialize(); } brokerV1String += "}"; Sinful s( brokerV1String.c_str() ); std::string ccbAddress = s.getCCBAddressString(); CCBID ccbID; if(! CCBServer::CCBIDFromString( ccbID, brokerCCBIDs[i].c_str() )) { m_valid = false; return; } MyString ccbContactString; CCBServer::CCBIDToContactString( ccbAddress.c_str(), ccbID, ccbContactString ); ccbList.append( ccbContactString.c_str() ); } if(! ccbList.isEmpty() ) { char * ccbID = ccbList.print_to_delimed_string( " " ); ASSERT( ccbID != NULL ); setCCBContact( ccbID ); free( ccbID ); } // Determine the set of public addresses. for( unsigned i = 0; i < v.size(); ++i ) { if( v[i].getProtocol() == CP_PRIMARY ) { continue; } if(! v[i].getCCBID().empty()) { continue; } if( v[i].getNetworkName() == PUBLIC_NETWORK_NAME ) { addAddrToAddrs( v[i].getSockAddr() ); } } // Determine the private network address, if any. for( unsigned i = 0; i < v.size(); ++i ) { if(! v[i].getCCBID().empty()) { continue; } if( v[i].getNetworkName() == PUBLIC_NETWORK_NAME ) { continue; } // A route with a public address may have a private network name // as a result of bypassing CCB. Those addresses are not private // addresses, so ignore them. condor_sockaddr sa = v[i].getSockAddr(); if( std::find( addrs.begin(), addrs.end(), sa ) != addrs.end() ) { continue; } // There can be only one private address. if( getPrivateAddr() != NULL ) { m_valid = false; return; } // setPrivateAddr( Sinful::privateAddressString( v[i].getSockAddr(), getSharedPortID() ).c_str() ); Sinful p( v[i].getSockAddr().to_ip_and_port_string().c_str() ); p.setSharedPortID( getSharedPortID() ); setPrivateAddr( p.getSinful() ); } // Set noUDP if any route sets it. for( unsigned i = 0; i < v.size(); ++i ) { if( v[i].getNoUDP() ) { setNoUDP( true ); break; } } m_valid = true; }
int main (int argc, char *argv[]) { #if !defined(WIN32) install_sig_handler(SIGPIPE, (SIG_HANDLER)SIG_IGN ); #endif // initialize to read from config file myDistro->Init( argc, argv ); myName = argv[0]; config(); dprintf_config_tool_on_error(0); // The arguments take two passes to process --- the first pass // figures out the mode, after which we can instantiate the required // query object. We add implied constraints from the command line in // the second pass. firstPass (argc, argv); // if the mode has not been set, it is STARTD_NORMAL if (mode == MODE_NOTSET) { setMode (MODE_STARTD_NORMAL, 0, DEFAULT); } // instantiate query object if (!(query = new CondorQuery (type))) { dprintf_WriteOnErrorBuffer(stderr, true); fprintf (stderr, "Error: Out of memory\n"); exit (1); } // if a first-pass setMode set a mode_constraint, apply it now to the query object if (mode_constraint && ! explicit_format) { query->addANDConstraint(mode_constraint); } // set pretty print style implied by the type of entity being queried // but do it with default priority, so that explicitly requested options // can override it switch (type) { #ifdef HAVE_EXT_POSTGRESQL case QUILL_AD: setPPstyle(PP_QUILL_NORMAL, 0, DEFAULT); break; #endif /* HAVE_EXT_POSTGRESQL */ case DEFRAG_AD: setPPstyle(PP_GENERIC_NORMAL, 0, DEFAULT); break; case STARTD_AD: setPPstyle(PP_STARTD_NORMAL, 0, DEFAULT); break; case SCHEDD_AD: setPPstyle(PP_SCHEDD_NORMAL, 0, DEFAULT); break; case MASTER_AD: setPPstyle(PP_MASTER_NORMAL, 0, DEFAULT); break; case CKPT_SRVR_AD: setPPstyle(PP_CKPT_SRVR_NORMAL, 0, DEFAULT); break; case COLLECTOR_AD: setPPstyle(PP_COLLECTOR_NORMAL, 0, DEFAULT); break; case STORAGE_AD: setPPstyle(PP_STORAGE_NORMAL, 0, DEFAULT); break; case NEGOTIATOR_AD: setPPstyle(PP_NEGOTIATOR_NORMAL, 0, DEFAULT); break; case GRID_AD: setPPstyle(PP_GRID_NORMAL, 0, DEFAULT); break; case GENERIC_AD: setPPstyle(PP_GENERIC, 0, DEFAULT); break; case ANY_AD: setPPstyle(PP_ANY_NORMAL, 0, DEFAULT); break; default: setPPstyle(PP_VERBOSE, 0, DEFAULT); } // set the constraints implied by the mode switch (mode) { #ifdef HAVE_EXT_POSTGRESQL case MODE_QUILL_NORMAL: #endif /* HAVE_EXT_POSTGRESQL */ case MODE_DEFRAG_NORMAL: case MODE_STARTD_NORMAL: case MODE_MASTER_NORMAL: case MODE_CKPT_SRVR_NORMAL: case MODE_SCHEDD_NORMAL: case MODE_SCHEDD_SUBMITTORS: case MODE_COLLECTOR_NORMAL: case MODE_NEGOTIATOR_NORMAL: case MODE_STORAGE_NORMAL: case MODE_GENERIC_NORMAL: case MODE_ANY_NORMAL: case MODE_GRID_NORMAL: case MODE_HAD_NORMAL: break; case MODE_OTHER: // tell the query object what the type we're querying is query->setGenericQueryType(genericType); free(genericType); genericType = NULL; break; case MODE_STARTD_AVAIL: // For now, -avail shows you machines avail to anyone. sprintf (buffer, "%s == \"%s\"", ATTR_STATE, state_to_string(unclaimed_state)); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addORConstraint (buffer); break; case MODE_STARTD_RUN: sprintf (buffer, "%s == \"%s\"", ATTR_STATE, state_to_string(claimed_state)); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addORConstraint (buffer); break; case MODE_STARTD_COD: sprintf (buffer, "%s > 0", ATTR_NUM_COD_CLAIMS ); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addORConstraint (buffer); break; default: break; } if(javaMode) { sprintf( buffer, "%s == TRUE", ATTR_HAS_JAVA ); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addANDConstraint (buffer); projList.AppendArg(ATTR_HAS_JAVA); projList.AppendArg(ATTR_JAVA_MFLOPS); projList.AppendArg(ATTR_JAVA_VENDOR); projList.AppendArg(ATTR_JAVA_VERSION); } if(offlineMode) { query->addANDConstraint( "size( OfflineUniverses ) != 0" ); projList.AppendArg( "OfflineUniverses" ); // // Since we can't add a regex to a projection, explicitly list all // the attributes we know about. // projList.AppendArg( "HasVM" ); projList.AppendArg( "VMOfflineReason" ); projList.AppendArg( "VMOfflineTime" ); } if(absentMode) { sprintf( buffer, "%s == TRUE", ATTR_ABSENT ); if (diagnose) { printf( "Adding constraint %s\n", buffer ); } query->addANDConstraint( buffer ); projList.AppendArg( ATTR_ABSENT ); projList.AppendArg( ATTR_LAST_HEARD_FROM ); projList.AppendArg( ATTR_CLASSAD_LIFETIME ); } if(vmMode) { sprintf( buffer, "%s == TRUE", ATTR_HAS_VM); if (diagnose) { printf ("Adding constraint [%s]\n", buffer); } query->addANDConstraint (buffer); projList.AppendArg(ATTR_VM_TYPE); projList.AppendArg(ATTR_VM_MEMORY); projList.AppendArg(ATTR_VM_NETWORKING); projList.AppendArg(ATTR_VM_NETWORKING_TYPES); projList.AppendArg(ATTR_VM_HARDWARE_VT); projList.AppendArg(ATTR_VM_AVAIL_NUM); projList.AppendArg(ATTR_VM_ALL_GUEST_MACS); projList.AppendArg(ATTR_VM_ALL_GUEST_IPS); projList.AppendArg(ATTR_VM_GUEST_MAC); projList.AppendArg(ATTR_VM_GUEST_IP); } // second pass: add regular parameters and constraints if (diagnose) { printf ("----------\n"); } secondPass (argc, argv); // initialize the totals object if (ppStyle == PP_CUSTOM && using_print_format) { if (pmHeadFoot & HF_NOSUMMARY) ppTotalStyle = PP_CUSTOM; } else { ppTotalStyle = ppStyle; } TrackTotals totals(ppTotalStyle); // fetch the query QueryResult q; if ((mode == MODE_STARTD_NORMAL) && (ppStyle == PP_STARTD_NORMAL)) { projList.AppendArg("Name"); projList.AppendArg("Machine"); projList.AppendArg("Opsys"); projList.AppendArg("Arch"); projList.AppendArg("State"); projList.AppendArg("Activity"); projList.AppendArg("LoadAvg"); projList.AppendArg("Memory"); projList.AppendArg("ActvtyTime"); projList.AppendArg("MyCurrentTime"); projList.AppendArg("EnteredCurrentActivity"); } else if( ppStyle == PP_VERBOSE ) { // Remove everything from the projection list if we're displaying // the "long form" of the ads. projList.Clear(); // but if -attributes was supplied, show only those attributes if ( ! dashAttributes.isEmpty()) { const char * s; dashAttributes.rewind(); while ((s = dashAttributes.next())) { projList.AppendArg(s); } } } if( projList.Count() > 0 ) { char **attr_list = projList.GetStringArray(); query->setDesiredAttrs(attr_list); deleteStringArray(attr_list); } // if diagnose was requested, just print the query ad if (diagnose) { ClassAd queryAd; // print diagnostic information about inferred internal state setMode ((Mode) 0, 0, NULL); setType (NULL, 0, NULL); setPPstyle ((ppOption) 0, 0, DEFAULT); printf ("----------\n"); q = query->getQueryAd (queryAd); fPrintAd (stdout, queryAd); printf ("----------\n"); fprintf (stderr, "Result of making query ad was: %d\n", q); exit (1); } // Address (host:port) is taken from requested pool, if given. char* addr = (NULL != pool) ? pool->addr() : NULL; Daemon* requested_daemon = pool; // If we're in "direct" mode, then we attempt to locate the daemon // associated with the requested subsystem (here encoded by value of mode) // In this case the host:port of pool (if given) denotes which // pool is being consulted if( direct ) { Daemon *d = NULL; switch( mode ) { case MODE_MASTER_NORMAL: d = new Daemon( DT_MASTER, direct, addr ); break; case MODE_STARTD_NORMAL: case MODE_STARTD_AVAIL: case MODE_STARTD_RUN: case MODE_STARTD_COD: d = new Daemon( DT_STARTD, direct, addr ); break; #ifdef HAVE_EXT_POSTGRESQL case MODE_QUILL_NORMAL: d = new Daemon( DT_QUILL, direct, addr ); break; #endif /* HAVE_EXT_POSTGRESQL */ case MODE_SCHEDD_NORMAL: case MODE_SCHEDD_SUBMITTORS: d = new Daemon( DT_SCHEDD, direct, addr ); break; case MODE_NEGOTIATOR_NORMAL: d = new Daemon( DT_NEGOTIATOR, direct, addr ); break; case MODE_CKPT_SRVR_NORMAL: case MODE_COLLECTOR_NORMAL: case MODE_LICENSE_NORMAL: case MODE_STORAGE_NORMAL: case MODE_GENERIC_NORMAL: case MODE_ANY_NORMAL: case MODE_OTHER: case MODE_GRID_NORMAL: case MODE_HAD_NORMAL: // These have to go to the collector, anyway. break; default: fprintf( stderr, "Error: Illegal mode %d\n", mode ); exit( 1 ); break; } // Here is where we actually override 'addr', if we can obtain // address of the requested daemon/subsys. If it can't be // located, then fail with error msg. // 'd' will be null (unset) if mode is one of above that must go to // collector (MODE_ANY_NORMAL, MODE_COLLECTOR_NORMAL, etc) if (NULL != d) { if( d->locate() ) { addr = d->addr(); requested_daemon = d; } else { const char* id = d->idStr(); if (NULL == id) id = d->name(); dprintf_WriteOnErrorBuffer(stderr, true); if (NULL == id) id = "daemon"; fprintf(stderr, "Error: Failed to locate %s\n", id); fprintf(stderr, "%s\n", d->error()); exit( 1 ); } } } ClassAdList result; CondorError errstack; if (NULL != ads_file) { MyString req; // query requirements q = query->getRequirements(req); const char * constraint = req.empty() ? NULL : req.c_str(); if (read_classad_file(ads_file, result, constraint)) { q = Q_OK; } } else if (NULL != addr) { // this case executes if pool was provided, or if in "direct" mode with // subsystem that corresponds to a daemon (above). // Here 'addr' represents either the host:port of requested pool, or // alternatively the host:port of daemon associated with requested subsystem (direct mode) q = query->fetchAds (result, addr, &errstack); } else { // otherwise obtain list of collectors and submit query that way CollectorList * collectors = CollectorList::create(); q = collectors->query (*query, result, &errstack); delete collectors; } // if any error was encountered during the query, report it and exit if (Q_OK != q) { dprintf_WriteOnErrorBuffer(stderr, true); // we can always provide these messages: fprintf( stderr, "Error: %s\n", getStrQueryResult(q) ); fprintf( stderr, "%s\n", errstack.getFullText(true).c_str() ); if ((NULL != requested_daemon) && ((Q_NO_COLLECTOR_HOST == q) || (requested_daemon->type() == DT_COLLECTOR))) { // Specific long message if connection to collector failed. const char* fullhost = requested_daemon->fullHostname(); if (NULL == fullhost) fullhost = "<unknown_host>"; const char* daddr = requested_daemon->addr(); if (NULL == daddr) daddr = "<unknown>"; char info[1000]; sprintf(info, "%s (%s)", fullhost, daddr); printNoCollectorContact( stderr, info, !expert ); } else if ((NULL != requested_daemon) && (Q_COMMUNICATION_ERROR == q)) { // more helpful message for failure to connect to some daemon/subsys const char* id = requested_daemon->idStr(); if (NULL == id) id = requested_daemon->name(); if (NULL == id) id = "daemon"; const char* daddr = requested_daemon->addr(); if (NULL == daddr) daddr = "<unknown>"; fprintf(stderr, "Error: Failed to contact %s at %s\n", id, daddr); } // fail exit (1); } if (noSort) { // do nothing } else if (sortSpecs.empty()) { // default classad sorting result.Sort((SortFunctionType)lessThanFunc); } else { // User requested custom sorting expressions: // insert attributes related to custom sorting result.Open(); while (ClassAd* ad = result.Next()) { for (vector<SortSpec>::iterator ss(sortSpecs.begin()); ss != sortSpecs.end(); ++ss) { ss->expr->SetParentScope(ad); classad::Value v; ss->expr->Evaluate(v); stringstream vs; // This will properly render all supported value types, // including undefined and error, although current semantic // pre-filters classads where sort expressions are undef/err: vs << ((v.IsStringValue())?"\"":"") << v << ((v.IsStringValue())?"\"":""); ad->AssignExpr(ss->keyAttr.c_str(), vs.str().c_str()); // Save the full expr in case user wants to examine on output: ad->AssignExpr(ss->keyExprAttr.c_str(), ss->arg.c_str()); } } result.Open(); result.Sort((SortFunctionType)customLessThanFunc); } // output result prettyPrint (result, &totals); delete query; return 0; }
void XM::File::read(bool) { if(!isOpen()) return; seek(0); ByteVector magic = readBlock(17); // it's all 0x00 for stripped XM files: READ_ASSERT(magic == "Extended Module: " || magic == ByteVector(17, 0)); READ_STRING(d->tag.setTitle, 20); READ_BYTE_AS(escape); // in stripped XM files this is 0x00: READ_ASSERT(escape == 0x1A || escape == 0x00); READ_STRING(d->tag.setTrackerName, 20); READ_U16L(d->properties.setVersion); READ_U32L_AS(headerSize); READ_ASSERT(headerSize >= 4); unsigned short length = 0; unsigned short restartPosition = 0; unsigned short channels = 0; unsigned short patternCount = 0; unsigned short instrumentCount = 0; unsigned short flags = 0; unsigned short tempo = 0; unsigned short bpmSpeed = 0; StructReader header; header.u16L(length) .u16L(restartPosition) .u16L(channels) .u16L(patternCount) .u16L(instrumentCount) .u16L(flags) .u16L(tempo) .u16L(bpmSpeed); unsigned int count = header.read(*this, headerSize - 4U); unsigned int size = std::min(headerSize - 4U, (unsigned long)header.size()); READ_ASSERT(count == size); d->properties.setLengthInPatterns(length); d->properties.setRestartPosition(restartPosition); d->properties.setChannels(channels); d->properties.setPatternCount(patternCount); d->properties.setInstrumentCount(instrumentCount); d->properties.setFlags(flags); d->properties.setTempo(tempo); d->properties.setBpmSpeed(bpmSpeed); seek(60 + headerSize); // read patterns: for(unsigned short i = 0; i < patternCount; ++ i) { READ_U32L_AS(patternHeaderLength); READ_ASSERT(patternHeaderLength >= 4); unsigned char packingType = 0; unsigned short rowCount = 0; unsigned short dataSize = 0; StructReader pattern; pattern.byte(packingType).u16L(rowCount).u16L(dataSize); unsigned int count = pattern.read(*this, patternHeaderLength - 4U); READ_ASSERT(count == std::min(patternHeaderLength - 4U, (unsigned long)pattern.size())); seek(patternHeaderLength - (4 + count) + dataSize, Current); } StringList intrumentNames; StringList sampleNames; unsigned int sumSampleCount = 0; // read instruments: for(unsigned short i = 0; i < instrumentCount; ++ i) { READ_U32L_AS(instrumentHeaderSize); READ_ASSERT(instrumentHeaderSize >= 4); String instrumentName; unsigned char instrumentType = 0; unsigned short sampleCount = 0; StructReader instrument; instrument.string(instrumentName, 22).byte(instrumentType).u16L(sampleCount); // 4 for instrumentHeaderSize unsigned int count = 4 + instrument.read(*this, instrumentHeaderSize - 4U); READ_ASSERT(count == std::min(instrumentHeaderSize, (unsigned long)instrument.size() + 4)); unsigned long sampleHeaderSize = 0; long offset = 0; if(sampleCount > 0) { sumSampleCount += sampleCount; // wouldn't know which header size to assume otherwise: READ_ASSERT(instrumentHeaderSize >= count + 4 && readU32L(sampleHeaderSize)); // skip unhandeled header proportion: seek(instrumentHeaderSize - count - 4, Current); for(unsigned short j = 0; j < sampleCount; ++ j) { unsigned long sampleLength = 0; unsigned long loopStart = 0; unsigned long loopLength = 0; unsigned char volume = 0; unsigned char finetune = 0; unsigned char sampleType = 0; unsigned char panning = 0; unsigned char noteNumber = 0; unsigned char compression = 0; String sampleName; StructReader sample; sample.u32L(sampleLength) .u32L(loopStart) .u32L(loopLength) .byte(volume) .byte(finetune) .byte(sampleType) .byte(panning) .byte(noteNumber) .byte(compression) .string(sampleName, 22); unsigned int count = sample.read(*this, sampleHeaderSize); READ_ASSERT(count == std::min(sampleHeaderSize, (unsigned long)sample.size())); // skip unhandeled header proportion: seek(sampleHeaderSize - count, Current); offset += sampleLength; sampleNames.append(sampleName); } } else { offset = instrumentHeaderSize - count; } intrumentNames.append(instrumentName); seek(offset, Current); } d->properties.setSampleCount(sumSampleCount); String comment(intrumentNames.toString("\n")); if(!sampleNames.isEmpty()) { comment += "\n"; comment += sampleNames.toString("\n"); } d->tag.setComment(comment); }