예제 #1
0
int MP4_moov_uuid_parse(uint8_t *uuid_data, uint32_t data_len, char *title)
{
	uint8_t *data = uuid_data;
	
	uint32_t uuid_len = read_be32(data);
	data += sizeof(uint32_t);
	if ( uuid_len != data_len ) {
		return 0;
	}
	//printf("uuid len = %08lx\n", uuid_len);
	
	// "MTDT" tag expected
	uint32_t tag = read_be32(data);
	if ( tag != 0x4d544454 ) {
		return 0;
	}
	data += sizeof(uint32_t);
	//printf("tag = %08lx\n", tag);
	if ( read_be16(data) != 0x0001 ) {
		return 0;
	}
	data += sizeof(uint16_t);
	uint16_t title_len = (read_be16(data) - 10) / 2;
	data += sizeof(uint16_t);

	if ( read_be32(data) != 0x00000001 ) {
		return 0;
	}
	data += sizeof(uint32_t);
	
	uint16_t lang_code = read_be16(data);
	data += sizeof(uint16_t);
	
	if ( read_be16(data) != 0x0001 ) {
		return 0;
	}
	data += sizeof(uint16_t);

	//printf("title len = %04lx (%d) \n", title_len, title_len);
	if ( title_len > (data_len - (data - uuid_data)) ) {
		title_len = data_len - (data - uuid_data) - 1;
	}

	for(uint16_t i = 0; i < title_len; i++) {
		uint16_t wc = read_be16(data);
		//printf("\twc=%04x\n", wc);
		data += sizeof(uint16_t);
		title[i] = (char)(wc & 0xff);
	}
	title[title_len] = 0;

	return 1;
}
예제 #2
0
파일: key.cpp 프로젝트: 40a/git-crypt
void		Key_file::Entry::load (std::istream& in)
{
	while (true) {
		uint32_t	field_id;
		if (!read_be32(in, field_id)) {
			throw Malformed();
		}
		if (field_id == KEY_FIELD_END) {
			break;
		}
		uint32_t	field_len;
		if (!read_be32(in, field_len)) {
			throw Malformed();
		}

		if (field_id == KEY_FIELD_VERSION) {
			if (field_len != 4) {
				throw Malformed();
			}
			if (!read_be32(in, version)) {
				throw Malformed();
			}
		} else if (field_id == KEY_FIELD_AES_KEY) {
			if (field_len != AES_KEY_LEN) {
				throw Malformed();
			}
			in.read(reinterpret_cast<char*>(aes_key), AES_KEY_LEN);
			if (in.gcount() != AES_KEY_LEN) {
				throw Malformed();
			}
		} else if (field_id == KEY_FIELD_HMAC_KEY) {
			if (field_len != HMAC_KEY_LEN) {
				throw Malformed();
			}
			in.read(reinterpret_cast<char*>(hmac_key), HMAC_KEY_LEN);
			if (in.gcount() != HMAC_KEY_LEN) {
				throw Malformed();
			}
		} else if (field_id & 1) { // unknown critical field
			throw Incompatible();
		} else {
			// unknown non-critical field - safe to ignore
			if (field_len > MAX_FIELD_LEN) {
				throw Malformed();
			}
			in.ignore(field_len);
			if (in.gcount() != static_cast<std::streamsize>(field_len)) {
				throw Malformed();
			}
		}
	}
}
예제 #3
0
파일: key.cpp 프로젝트: 40a/git-crypt
void		Key_file::load_header (std::istream& in)
{
	while (true) {
		uint32_t	field_id;
		if (!read_be32(in, field_id)) {
			throw Malformed();
		}
		if (field_id == HEADER_FIELD_END) {
			break;
		}
		uint32_t	field_len;
		if (!read_be32(in, field_len)) {
			throw Malformed();
		}

		if (field_id == HEADER_FIELD_KEY_NAME) {
			if (field_len > KEY_NAME_MAX_LEN) {
				throw Malformed();
			}
			if (field_len == 0) {
				// special case field_len==0 to avoid possible undefined behavior
				// edge cases with an empty std::vector (particularly, &bytes[0]).
				key_name.clear();
			} else {
				std::vector<char>	bytes(field_len);
				in.read(&bytes[0], field_len);
				if (in.gcount() != static_cast<std::streamsize>(field_len)) {
					throw Malformed();
				}
				key_name.assign(&bytes[0], field_len);
			}
			if (!validate_key_name(key_name.c_str())) {
				key_name.clear();
				throw Malformed();
			}
		} else if (field_id & 1) { // unknown critical field
			throw Incompatible();
		} else {
			// unknown non-critical field - safe to ignore
			if (field_len > MAX_FIELD_LEN) {
				throw Malformed();
			}
			in.ignore(field_len);
			if (in.gcount() != static_cast<std::streamsize>(field_len)) {
				throw Malformed();
			}
		}
	}
}
예제 #4
0
    fluidStream(std::istream *_fstream)
      : alureStream(_fstream), Divisions(100),
        format(AL_NONE), sampleRate(48000), samplesPerTick(1.),
        fluidSettings(NULL), fluidSynth(NULL), fontID(FLUID_FAILED),
        doFontLoad(true)
    {
        if(!fsynth_handle) return;

        ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
        if(device) alcGetIntegerv(device, ALC_FREQUENCY, 1, &sampleRate);

        char hdr[4];
        if(!fstream->read(hdr, 4))
            return;

        if(memcmp(hdr, "MThd", 4) == 0)
        {
            ALuint len = read_be32(fstream);
            if(len != 6)
                return;

            int type = read_be16(fstream);
            if(type != 0 && type != 1)
                return;

            ALuint numtracks = read_be16(fstream);

            Divisions = read_be16(fstream);
            UpdateTempo(500000);

            Tracks.resize(numtracks);
            for(std::vector<MidiTrack>::iterator i = Tracks.begin(), end = Tracks.end();i != end;i++)
            {
                if(!fstream->read(hdr, 4) || memcmp(hdr, "MTrk", 4) != 0)
                    return;

                ALint len = read_be32(fstream);
                i->data.resize(len);
                if(!fstream->read(reinterpret_cast<char*>(&i->data[0]), len) ||
                   fstream->gcount() != len)
                    return;

                unsigned long val = i->ReadVarLen();
                i->SamplesLeft += val * samplesPerTick;
            }
            SetupSynth();
        }
    }
예제 #5
0
    aiffStream(std::istream *_fstream)
        : alureStream(_fstream), format(0), dataStart(0)
    {
        ALubyte buffer[25];
        int length;

        if(!fstream->read(reinterpret_cast<char*>(buffer), 12) ||
                memcmp(buffer, "FORM", 4) != 0 || memcmp(buffer+8, "AIFF", 4) != 0)
            return;

        while(!dataStart || format == AL_NONE)
        {
            char tag[4];
            if(!fstream->read(tag, 4))
                break;

            /* read chunk length */
            length = read_be32(fstream);

            if(memcmp(tag, "COMM", 4) == 0 && length >= 18)
            {
                /* mono or stereo data */
                channels = read_be16(fstream);

                /* number of sample frames */
                fstream->ignore(4);

                /* bits per sample */
                sampleSize = read_be16(fstream) / 8;

                /* sample frequency */
                samplerate = read_be80extended(fstream);

                /* block alignment */
                blockAlign = channels * sampleSize;

                format = GetSampleFormat(channels, sampleSize*8, false);

                length -= 18;
            }
            else if(memcmp(tag, "SSND", 4) == 0)
            {
                dataStart = fstream->tellg();
                dataStart += 8;
                dataLen = remLen = length - 8;
            }

            fstream->seekg(length, std::ios_base::cur);
        }

        if(dataStart > 0 && format != AL_NONE)
            fstream->seekg(dataStart);
    }
예제 #6
0
static vod_status_t
mp4_decrypt_start_frame(void* ctx, input_frame_t* frame)
{
	mp4_decrypt_state_t* state = ctx;
	vod_status_t rc;

	rc = state->frames_source->start_frame(state->frames_source_context, frame);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the iv
	if (state->auxiliary_info_pos + MP4_AES_CTR_IV_SIZE > state->auxiliary_info_end)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"mp4_decrypt_start_frame: failed to get iv from auxiliary info");
		return VOD_BAD_DATA;
	}

	mp4_aes_ctr_set_iv(&state->cipher, state->auxiliary_info_pos);
	state->auxiliary_info_pos += MP4_AES_CTR_IV_SIZE;

	if (!state->use_subsamples)
	{
		state->encrypted_bytes = UINT_MAX;
		return VOD_OK;
	}

	// get the subsample info
	if (state->auxiliary_info_pos + sizeof(uint16_t) + sizeof(cenc_sample_auxiliary_data_subsample_t) > state->auxiliary_info_end)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"mp4_decrypt_start_frame: failed to get subsample info from auxiliary info");
		return VOD_BAD_DATA;
	}

	read_be16(state->auxiliary_info_pos, state->subsample_count);
	if (state->subsample_count <= 0)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"mp4_decrypt_start_frame: invalid subsample count");
		return VOD_BAD_DATA;
	}

	read_be16(state->auxiliary_info_pos, state->clear_bytes);
	read_be32(state->auxiliary_info_pos, state->encrypted_bytes);

	state->subsample_count--;

	return VOD_OK;
}
예제 #7
0
static void vh_calc_checksum(struct volume_header *vh)
{
	uint32_t newsum = 0;
	unsigned char *buffer = (unsigned char *)vh;
	unsigned int i;

	vh->vh_csum = 0;

	for(i = 0; i < sizeof(struct volume_header); i += 4)
        newsum -= read_be32(&buffer[i]);

    write_be32(newsum, (unsigned char *)&vh->vh_csum);
}
예제 #8
0
파일: doctor.cpp 프로젝트: pdh11/chorale
static unsigned CheckID3v2(util::Stream *st, uint64_t *pos, bool *ok)
{
    *pos = 0;

    char header[10];
    unsigned rc = st->ReadAll(&header, 10);
    if (rc)
    {
	perror("read");
	return rc;
    }

    if (!memcmp(header, "ID3", 3))
    {
	unsigned char version = header[3];
	if (version < 2 || version > 4)
	{
	    printf("  Bad ID3v2\n");
	    *ok = false;
	}
	else
	{
	    unsigned len = (header[6] << 21)
		| (header[7] << 14)
		| (header[8] << 7)
		| (header[9]);
	    
	    uint64_t sz = st->GetLength();
    
	    if (len > sz-10)
	    {
		printf("  Bad ID3v2\n");
		*ok = false;
	    }
	    if (s_all)
	    {
		printf("  %u bytes of ID3v2.%u\n", len, version);
	    }
	    unsigned flags = header[4];

	    uint64_t frame = 0;
	    while (frame < len)
	    {
		unsigned char frameheader[10];
		st->Seek(10 + frame);
		rc = st->ReadAll(&frameheader, version == 2 ? 6 : 10);
		if (frameheader[0] == 0)
		{
		    if (s_all)
			printf("    %u bytes of padding\n", len-frame);
		    break;
		}

		unsigned framelen = 0;
		if (version == 4)
		{
		    framelen = read_unsync32(frameheader+4);
		}
		else if (version == 3)
		{
		    framelen = read_be32(frameheader+4);
		} 
		else if (version == 2)
		{
		    framelen = read_be24(frameheader+3);
		}

		if (framelen)
		{
		    std::string tag(frameheader, 
				    frameheader + ((version==2) ? 3 : 4));
		    printf("    %u-byte frame (%s)\n", framelen, tag.c_str());
		}
		else
		{
		    *ok = false;
		    printf("    Bad frame length %u\n", framelen);
		}
		
		frame += framelen + ((version==2) ? 6 : 10);
	    }

	    *pos = len+10;
	}
    }

    return 0;
}
예제 #9
0
파일: doctor.cpp 프로젝트: pdh11/chorale
static unsigned WalkFrames(util::Stream *st, uint64_t pos, bool *ok)
{
    uint64_t end = st->GetLength();
    unsigned int nframes = 0;
    unsigned int nsilent = 0;
    bool started = false;
    unsigned int bitrate = 0;
    bool vbr = false;
    uint64_t streamlen = end-pos;

    while (pos < (end-4))
    {
	unsigned char header[4];
	st->Seek(pos);
	unsigned rc = st->ReadAll(header, 4);
	if (rc)
	    return rc;

	FrameHeader fh;
	fh.x = read_be32(header);
	if (fh.s.sync != 0x7FF)
	{
	    printf("  Sync lost at %llu (%08x %08x)\n", (long long unsigned)pos,
		   fh.x, fh.s.sync);
	    *ok = false;
	    return 0;
	}
	
	unsigned int len = FrameLength(fh);
	if (!len)
	{
	    printf("  Bogus header %08x at %llu\n", fh.x,
		   (long long unsigned)pos);
	    *ok = false;
	    return 0;
	}

	if (s_frames)
	{
	    printf("  0x%08llx: frame %u\n", (unsigned long long)pos, nframes);
	}

	unsigned char frame[8192];

	rc = st->ReadAll(frame, len-4);
	if (rc)
	    return rc;

	unsigned int zeroes;
	if (fh.s.version == 3)
	{
	    if (fh.s.channelmode == 3)
		zeroes = 17;
	    else
		zeroes = 32;
	} 
	else
	{
	    if (fh.s.channelmode == 3)
		zeroes = 9;
	    else
		zeroes = 17;
	}

	bool silent = true;
	for (unsigned int i=0; i<zeroes && silent; ++i)
	{
	    if (frame[i])
		silent = false;
	}

	if (silent && !started)
	{
	    ++nsilent;
	    if (!memcmp(frame + zeroes, "Xing", 4)
		|| !memcmp(frame + zeroes, "Info", 4))
	    {
		if (s_all)
		{
		    printf("  Info header in frame %u\n", nframes);
		    printf("    Frame count %u\n", read_be32(frame+zeroes+8));
		    printf("    Stream length %u\n", read_be32(frame+zeroes+12));
		}
	    }
	    else if (!memcmp(frame+32, "VBRI", 4))
	    {
		if (s_all)
		    printf("  VBRI header in frame %u\n", nframes);
	    }
	    else
	    {
		started = true;
		if (s_all)
		    printf("  Audio begins in frame %u\n", nframes);
	    }
	}
	else
	{
	    if (!started)
	    {
		started = true;
		if (s_all)
		    printf("  Audio begins in frame %u\n", nframes);
	    }
	    nsilent = 0;
	}

	if (started)
	{
	    unsigned frame_bitrate = BitRate(fh);
	    if (!bitrate)
		bitrate = frame_bitrate;
	    else if (bitrate != frame_bitrate)
		vbr = true;
	}

	++nframes;

	pos += len;
    }

    if (s_all)
    {
	if (nsilent)
	    printf("  Final %u frames silent\n", nsilent);
	printf("  %u frames\n", nframes);
	if (vbr)
	    printf("  Variable bitrate\n");
	else
	    printf("  Bitrate=%u CBR\n", bitrate);
	printf("  MP3 stream length %llu\n", (unsigned long long)streamlen);
    }

    return 0;
}
예제 #10
0
static vod_status_t
mp4_decrypt_process(
	mp4_decrypt_state_t* state, 
	size_t size)
{
	u_char* dest = state->output_pos;
	u_char* src = state->input_pos;
	vod_status_t rc;
	size_t cur_size;

	while (size > 0)
	{
		if (state->clear_bytes <= 0 && state->encrypted_bytes <= 0)
		{
			// finished a subsample, read the next one
			if (state->subsample_count <= 0)
			{
				vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
					"mp4_decrypt_process: exhausted subsample bytes");
				return VOD_BAD_DATA;
			}

			if (state->auxiliary_info_pos + sizeof(cenc_sample_auxiliary_data_subsample_t) > state->auxiliary_info_end)
			{
				vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
					"mp4_decrypt_process: failed to get subsample info from auxiliary info");
				return VOD_BAD_DATA;
			}

			read_be16(state->auxiliary_info_pos, state->clear_bytes);
			read_be32(state->auxiliary_info_pos, state->encrypted_bytes);

			state->subsample_count--;
		}

		if (state->clear_bytes > 0)
		{
			// copy clear bytes
			cur_size = vod_min(state->clear_bytes, size);
			dest = vod_copy(dest, src, cur_size);
			src += cur_size;
			size -= cur_size;
			state->clear_bytes -= cur_size;
		}

		// decrypt encrypted bytes
		cur_size = vod_min(state->encrypted_bytes, size);
		rc = mp4_aes_ctr_process(&state->cipher, dest, src, cur_size);
		if (rc != VOD_OK)
		{
			return rc;
		}

		dest += cur_size;
		src += cur_size;
		size -= cur_size;
		state->encrypted_bytes -= cur_size;
	}

	state->output_pos = dest;
	state->input_pos = src;

	return VOD_OK;
}
예제 #11
0
파일: tpm.c 프로젝트: canistation/coreboot
size_t tpm2_process_command(const void *tpm2_command, size_t command_size,
			    void *tpm2_response, size_t max_response)
{
	uint32_t status;
	uint32_t expected_status_bits;
	size_t payload_size;
	size_t bytes_to_go;
	const uint8_t *cmd_body = tpm2_command;
	uint8_t *rsp_body = tpm2_response;
	union fifo_transfer_buffer fifo_buffer;
	const int HEADER_SIZE = 6;
	struct tpm2_info *tpm_info = car_get_var_ptr(&g_tpm_info);

	/* Do not try using an uninitialized TPM. */
	if (!tpm_info->vendor_id)
		return 0;

	/* Skip the two byte tag, read the size field. */
	payload_size = read_be32(cmd_body + 2);

	/* Sanity check. */
	if (payload_size != command_size) {
		printk(BIOS_ERR,
		       "Command size mismatch: encoded %zd != requested %zd\n",
		       payload_size, command_size);
		trace_dump("W", TPM_DATA_FIFO_REG, command_size, cmd_body, 1);
		printk(BIOS_DEBUG, "\n");
		return 0;
	}

	/* Let the TPM know that the command is coming. */
	write_tpm_sts(TPM_STS_COMMAND_READY);

	/*
	 * TPM commands and responses written to and read from the FIFO
	 * register (0x24) are datagrams of variable size, prepended by a 6
	 * byte header.
	 *
	 * The specification description of the state machine is a bit vague,
	 * but from experience it looks like there is no need to wait for the
	 * sts.expect bit to be set, at least with the 9670 and cr50 devices.
	 * Just write the command into FIFO, making sure not to exceed the
	 * burst count or the maximum PDU size, whatever is smaller.
	 */
	fifo_buffer.tx_buffer = cmd_body;
	fifo_transfer(command_size, fifo_buffer, fifo_transmit);

	/* Now tell the TPM it can start processing the command. */
	write_tpm_sts(TPM_STS_GO);

	/* Now wait for it to report that the response is ready. */
	expected_status_bits = TPM_STS_VALID | TPM_STS_DATA_AVAIL;
	if (!wait_for_status(expected_status_bits, expected_status_bits)) {
		/*
		 * If timed out, which should never happen, let's at least
		 * print out the offending command.
		 */
		trace_dump("W", TPM_DATA_FIFO_REG, command_size, cmd_body, 1);
		printk(BIOS_DEBUG, "\n");
		return 0;
	}

	/*
	 * The response is ready, let's read it. First we read the FIFO
	 * payload header, to see how much data to expect. The response header
	 * size is fixed to six bytes, the total payload size is stored in
	 * network order in the last four bytes.
	 */
	tpm2_read_reg(TPM_DATA_FIFO_REG, rsp_body, HEADER_SIZE);

	/* Find out the total payload size, skipping the two byte tag. */
	payload_size = read_be32(rsp_body + 2);

	if (payload_size > max_response) {
		/*
		 * TODO(vbendeb): at least drain the FIFO here or somehow let
		 * the TPM know that the response can be dropped.
		 */
		printk(BIOS_ERR, " TPM response too long (%zd bytes)",
		       payload_size);
		return 0;
	}

	/*
	 * Now let's read all but the last byte in the FIFO to make sure the
	 * status register is showing correct flow control bits: 'more data'
	 * until the last byte and then 'no more data' once the last byte is
	 * read.
	 */
	bytes_to_go = payload_size - 1 - HEADER_SIZE;
	fifo_buffer.rx_buffer = rsp_body + HEADER_SIZE;
	fifo_transfer(bytes_to_go, fifo_buffer, fifo_receive);

	/* Verify that there is still data to read. */
	read_tpm_sts(&status);
	if ((status & expected_status_bits) != expected_status_bits) {
		printk(BIOS_ERR, "unexpected intermediate status %#x\n",
		       status);
		return 0;
	}

	/* Read the last byte of the PDU. */
	tpm2_read_reg(TPM_DATA_FIFO_REG, rsp_body + payload_size - 1, 1);

	/* Terminate the dump, if enabled. */
	if (debug_level_)
		printk(BIOS_DEBUG, "\n");

	/* Verify that 'data available' is not asseretd any more. */
	read_tpm_sts(&status);
	if ((status & expected_status_bits) != TPM_STS_VALID) {
		printk(BIOS_ERR, "unexpected final status %#x\n", status);
		return 0;
	}

	/* Move the TPM back to idle state. */
	write_tpm_sts(TPM_STS_COMMAND_READY);

	return payload_size;
}
예제 #12
0
파일: cr50.c 프로젝트: canistation/coreboot
static int cr50_i2c_tis_recv(struct tpm_chip *chip, uint8_t *buf,
			     size_t buf_len)
{
	size_t burstcnt, current, len, expected;
	uint8_t addr = TPM_DATA_FIFO(chip->vendor.locality);
	uint8_t mask = TPM_STS_VALID | TPM_STS_DATA_AVAIL;
	int status;

	if (buf_len < TPM_HEADER_SIZE)
		goto out_err;

	if (cr50_i2c_wait_burststs(chip, mask, &burstcnt, &status) < 0) {
		printk(BIOS_ERR, "%s: First chunk not available\n", __func__);
		goto out_err;
	}

	/* Read first chunk of burstcnt bytes */
	if (cr50_i2c_read(chip, addr, buf, burstcnt) != 0) {
		printk(BIOS_ERR, "%s: Read failed\n", __func__);
		goto out_err;
	}

	/* Determine expected data in the return buffer */
	expected = read_be32(buf + TPM_RSP_SIZE_BYTE);
	if (expected > buf_len) {
		printk(BIOS_ERR, "%s: Too much data: %zu > %zu\n",
		       __func__, expected, buf_len);
		goto out_err;
	}

	/* Now read the rest of the data */
	current = burstcnt;
	while (current < expected) {
		/* Read updated burst count and check status */
		if (cr50_i2c_wait_burststs(chip, mask, &burstcnt, &status) < 0)
			goto out_err;

		len = min(burstcnt, expected - current);
		if (cr50_i2c_read(chip, addr, buf + current, len) != 0) {
			printk(BIOS_ERR, "%s: Read failed\n", __func__);
			goto out_err;
		}

		current += len;
	}

	/* Ensure TPM is done reading data */
	if (cr50_i2c_wait_burststs(chip, TPM_STS_VALID, &burstcnt, &status) < 0)
		goto out_err;
	if (status & TPM_STS_DATA_AVAIL) {
		printk(BIOS_ERR, "%s: Data still available\n", __func__);
		goto out_err;
	}

	return current;

out_err:
	/* Abort current transaction if still pending */
	if (cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)
		cr50_i2c_tis_ready(chip);
	return -1;
}
예제 #13
0
blargg_err_t Nsfe_Emu::load( const header_t& nsfe_tag, Emu_Reader& in )
{
	// check header
	if ( memcmp( nsfe_tag.tag, "NSFE", 4 ) )
		return "Not an NSFE file";
	
	// free previous info
	track_name_data.clear();
	track_names.clear();
	playlist.clear();
	track_times.clear();
	
	// default nsf header
	static const Nsf_Emu::header_t base_header =
	{
		'N','E','S','M','\x1A', // tag
		1,              // version
		1, 1,           // track count, first track
		0,0,0,0,0,0,    // addresses
		"","","",       // strings
		{ 0x1A, 0x41 }, // NTSC rate
		{ 0 },          // banks
		{ 0x20, 0x4E }, // PAL rate
		0,0,            // flags
		0,0,0,0         // unused
	};
	Nsf_Emu::header_t& header = info_;
	header = base_header;
	
	// parse tags
	int phase = 0;
	while ( phase != 3 )
	{
		// read size and tag
		long size = 0;
		long tag = 0;
		BLARGG_RETURN_ERR( read_le32( in, &size ) );
		BLARGG_RETURN_ERR( read_be32( in, &tag ) );
		
		switch ( tag )
		{
			case 'INFO': {
				check( phase == 0 );
				if ( size < 8 )
					return "Bad NSFE file";
				
				nsfe_info_t info;
				info.track_count = 1;
				info.first_track = 0;
				
				int s = size;
				if ( s > sizeof info )
					s = sizeof info;
				BLARGG_RETURN_ERR( in.read( &info, s ) );
				BLARGG_RETURN_ERR( in.skip( size - s ) );
				phase = 1;
				info_.speed_flags = info.speed_flags;
				info_.chip_flags = info.chip_flags;
				info_.track_count = info.track_count;
				info_.first_track = info.first_track;
				std::memcpy( info_.load_addr, info.load_addr, 2 * 3 );
				break;
			}
			
			case 'BANK':
				if ( size > sizeof info_.banks )
					return "Bad NSFE file";
				BLARGG_RETURN_ERR( in.read( info_.banks, size ) );
				break;
			
			case 'auth': {
				std::vector<char> chars;
				std::vector<const char*> strs;
				BLARGG_RETURN_ERR( read_strs( in, size, chars, strs ) );
				int n = strs.size();
				
				if ( n > 3 )
					copy_str( strs [3], info_.ripper, sizeof info_.ripper );
				
				if ( n > 2 )
					copy_str( strs [2], info_.copyright, sizeof info_.copyright );
				
				if ( n > 1 )
					copy_str( strs [1], info_.author, sizeof info_.author );
				
				if ( n > 0 )
					copy_str( strs [0], info_.game, sizeof info_.game );
				
				break;
			}
			
			case 'time': {
				track_times.resize( size / 4 );
				for ( int i = 0; i < track_times.size(); i++ )
					BLARGG_RETURN_ERR( read_le32( in, &track_times [i] ) );
				break;
			}
			
			case 'tlbl':
				BLARGG_RETURN_ERR( read_strs( in, size, track_name_data, track_names ) );
				break;
			
			case 'plst':
				playlist.resize( size );
				BLARGG_RETURN_ERR( in.read( &(*playlist.begin()), size ) );
				break;
			
			case 'DATA': {
				check( phase == 1 );
				phase = 2;
				Subset_Reader sub( &in, size ); // limit emu to nsf data
				BLARGG_RETURN_ERR( Nsf_Emu::load( info_, sub ) );
				check( sub.remain() == 0 );
				break;
			}
			
			case 'NEND':
				check( phase == 2 );
				phase = 3;
				break;
			
			default:
				// tags that can be skipped start with a lowercase character
				check( islower( (tag >> 24) & 0xff ) );
				BLARGG_RETURN_ERR( in.skip( size ) );
				break;
		}
	}
	
	return blargg_success;
}