示例#1
0
EMUFILE_FILE* FCEUD_UTF8_fstream(const char *n, const char *m)
{
	if(strchr(m, 'w') || strchr(m, '+'))
	{
		FCEUD_MakePathDirs(n);
	}

	EMUFILE_FILE *fs = new EMUFILE_FILE(n,m);
	if(!fs->is_open()) {
		delete fs;
		return 0;
	} else return fs;
}
示例#2
0
文件: file.cpp 项目: cdenix/fceu-ps3
FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, char *mode, char *ext, int index, const char** extensions)
{
	FILE *ipsfile=0;
	FCEUFILE *fceufp=0;

	bool read = (std::string)mode == "rb";
	bool write = (std::string)mode == "wb";
	if((read&&write) || (!read&&!write))
	{
		FCEU_PrintError("invalid file open mode specified (only wb and rb are supported)");
		return 0;
	}

	std::string archive,fname,fileToOpen;
	FCEU_SplitArchiveFilename(path,archive,fname,fileToOpen);
	

	//try to setup the ips file
	if(ipsfn && read)
		ipsfile=FCEUD_UTF8fopen(ipsfn,"rb");
	if(read)
	{
		ArchiveScanRecord asr = FCEUD_ScanArchive(fileToOpen);
		asr.files.FilterByExtension(extensions);
		if(!asr.isArchive())
		{
			//if the archive contained no files, try to open it the old fashioned way
			EMUFILE_FILE* fp = FCEUD_UTF8_fstream(fileToOpen,mode);
			if(!fp || (fp->get_fp() == NULL))
			{
				return 0;
			}

			//try to read a zip file
			{
				fceufp = TryUnzip(fileToOpen);
				if(fceufp) {
					delete fp;
					fceufp->filename = fileToOpen;
					fceufp->logicalPath = fileToOpen;
					fceufp->fullFilename = fileToOpen;
					fceufp->archiveIndex = -1;
					goto applyips;
				}
			}

			//try to read a gzipped file
			{
				uint32 magic;
				
				magic = fp->fgetc();
				magic|=fp->fgetc()<<8;
				magic|=fp->fgetc()<<16;
				fp->fseek(0,SEEK_SET);

				if(magic==0x088b1f) {
					 // maybe gzip... 

					void* gzfile = gzopen(fileToOpen.c_str(),"rb");
					if(gzfile) {
						delete fp;

						int size;
						for(size=0; gzgetc(gzfile) != EOF; size++) {}
						EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(size);
						gzseek(gzfile,0,SEEK_SET);
						gzread(gzfile,ms->buf(),size);
						gzclose(gzfile);

						fceufp = new FCEUFILE();
						fceufp->filename = fileToOpen;
						fceufp->logicalPath = fileToOpen;
						fceufp->fullFilename = fileToOpen;
						fceufp->archiveIndex = -1;
						fceufp->stream = ms;
						fceufp->size = size;
						goto applyips;
					}
				}
			}

		
			//open a plain old file
			fceufp = new FCEUFILE();
			fceufp->filename = fileToOpen;
			fceufp->logicalPath = fileToOpen;
			fceufp->fullFilename = fileToOpen;
			fceufp->archiveIndex = -1;
			fceufp->stream = fp;
			FCEU_fseek(fceufp,0,SEEK_END);
			fceufp->size = FCEU_ftell(fceufp);
			FCEU_fseek(fceufp,0,SEEK_SET);
			goto applyips;
		}
		else
		{
			//open an archive file
			if(archive == "")
				if(index != -1)
					fceufp = FCEUD_OpenArchiveIndex(asr, fileToOpen, index);
				else 
					fceufp = FCEUD_OpenArchive(asr, fileToOpen, 0);
			else
				fceufp = FCEUD_OpenArchive(asr, archive, &fname);

			if(!fceufp) return 0;

			FileBaseInfo fbi = DetermineFileBase(fileToOpen);
			fceufp->logicalPath = fbi.filebasedirectory + fceufp->filename;
			goto applyips;
		}

	applyips:
		//try to open the ips file
		if(!ipsfile && !ipsfn)
			ipsfile=FCEUD_UTF8fopen(FCEU_MakeIpsFilename(DetermineFileBase(fceufp->logicalPath.c_str())),"rb");
		ApplyIPS(ipsfile,fceufp);
		return fceufp;
	}
	return 0;
}
示例#3
0
文件: mc.cpp 项目: krysanto/desmume
BackupDevice::BackupDevice()
{
	size_t elements_read, elements_written;
	fpMC = NULL;
	fsize = 0;
	addr_size = 0;
	isMovieMode = false;

	//default for most games; will be altered where appropriate
	//usually 0xFF, but occasionally others. If these exceptions could be related to a particular backup memory type, that would be helpful.
	//at first we assumed it would be 0x00, but baby pals proved that it should be 0xFF:
	// the game reads its initial sound volumes from uninitialized data, and if it is 0, the game will be silent
	// if it is 0xFF then the game starts with its sound and music at max, as presumably it is supposed to.
	// so in r3303 we finally changed it (no$ appears definitely to initialize to 0xFF)
	uninitializedValue = 0xFF;

	if (gameInfo.romsize == 0) return;

	char buf[MAX_PATH] = {0};
	memset(buf, 0, MAX_PATH);
	path.getpathnoext(path.BATTERY, buf);
	filename = std::string(buf) + ".dsv";

	MCLOG("MC: %s\n", filename.c_str());

	bool fexists = (access(filename.c_str(), 0) == 0)?true:false;

	if (fexists && CommonSettings.backupSave)
	{
		std::string tmp_fsav = std::string(buf) + ".dsv.bak";
		EMUFILE_FILE *in = new EMUFILE_FILE(filename, "rb");
		if (!in->fail())
		{
			u32 sz = in->size();
			if (sz > 0)
			{
				EMUFILE_FILE *out = new EMUFILE_FILE(tmp_fsav, "wb");
				if (!out->fail())
				{
					u8 *data = new u8[sz];
					elements_read = fread(data, 1, sz, in->get_fp());
					elements_written = fwrite(data, 1, sz, out->get_fp());
					if (elements_read != sz || elements_written != sz)
						printf("Possibly incomplete data read/write.\n");
					delete [] data;
				}
				delete out;
			}
		}
		delete in;
	}

	if (!fexists)
	{
		printf("DeSmuME .dsv save file not found. Trying to load an old raw .sav file.\n");
		std::string tmp_fsav = std::string(buf) + ".sav";

		EMUFILE_FILE *fpTmp = new EMUFILE_FILE(tmp_fsav, "rb");
		if (!fpTmp->fail())
		{
			u32 sz = fpTmp->size();

			if (sz > 0)
			{
				EMUFILE_FILE *fpOut = new EMUFILE_FILE(filename, "wb");
				if (!fpOut->fail())
				{
					u8 *buf = new u8[sz + 1];
					if ((buf) && (fread(buf, 1, sz, fpTmp->get_fp()) == sz))
					{
						if (no_gba_unpack(buf, sz))
							printf("Converted from no$gba save.\n");
						else
							sz = trim(buf, sz);

						if (fwrite(buf, 1, sz, fpOut->get_fp()) == sz)
						{
							u8 res = searchFileSaveType(sz);
							if (res != 0xFF)
							{
								info.type = (res + 1);
								addr_size = info.addr_size = save_types[info.type].addr_size;
								info.size = fsize = sz;
								ensure(sz, fpOut);
								fsize = 0;
							}
							else
								info.type = 0;
							fexists = true;
						}
					}
					delete [] buf;
				}
				delete fpOut;
			}
		}
		delete fpTmp;
	}

	fpMC = new EMUFILE_FILE(filename, fexists?"rb+":"wb+");
	if (!fpMC->fail())
	{
		fsize = fpMC->size();
		if (fsize < saveSizes[0])
			fpMC->truncate(0);

		if (readFooter() == 0)
			fsize -= (strlen(kDesmumeSaveCookie) + strlen(DESMUME_BACKUP_FOOTER_TXT) + 24);
		else
		{
			memset(&info, 0, sizeof(info));
			fsize = 0;
		}
	
		fpMC->fseek(0, SEEK_SET);

		u32 left = 0;
		if (CommonSettings.autodetectBackupMethod == 1)
		{
			if (advsc.isLoaded())
			{
				info.type = advsc.getSaveType();
				if (info.type != 0xFF && info.type != 0xFE)
				{
					info.type++;
					u32 adv_size = save_types[info.type].size;
					if (info.size > adv_size)
					{
						info.size = adv_size;
						fpMC->truncate(adv_size);
						ensure(adv_size, fpMC);
					}
					else
						if (info.size < adv_size)
						{
							left = adv_size - info.size;
							info.size = adv_size;
							ensure(adv_size);
						}

					fsize = adv_size;
				}
			}
		}

		addr_size = info.addr_size;
		info.padSize = fsize;
		//none of the other fields are used right now

		if (CommonSettings.autodetectBackupMethod != 1 && info.type == 0) 
		{
			info.type = searchFileSaveType(info.size);
			if (info.type == 0xFF) info.type = 0;
		}

		u32 ss = (info.padSize * 8) / 1024;
		bool _Mbit = false;

		if (ss >= 1024)
		{
			ss /= 1024;
			_Mbit = true;
		}

		if (ss > 0)
			printf("Backup size: %u %cbit\n", ss, _Mbit?'M':'K');
	}

	state = (fsize > 0)?RUNNING:DETECTING;
	reset();
}
示例#4
0
文件: mc.cpp 项目: krysanto/desmume
void BackupDevice::ensure(u32 addr, u8 val, EMUFILE_FILE *fpOut)
{
	if (!fpOut && (addr < fsize)) return;

	EMUFILE_FILE *fp = fpOut?fpOut:fpMC;

#ifndef _DONT_SAVE_BACKUP
	fp->fseek(fsize, SEEK_SET);
#endif
	
	u32 padSize = pad_up_size(addr);
	u32 size = padSize - fsize;
	info.padSize = info.size = fsize = padSize;
	int type = searchFileSaveType(fsize);
	if (type != 0xFF) info.type = (type + 1);

#ifndef _DONT_SAVE_BACKUP
	if (size > 0)
	{
		u8 *tmp = new u8[size];
		memset(tmp, val, size);
		fwrite(tmp, 1, size, fp->get_fp());
		delete [] tmp;
	}

	//this is just for humans to read
	fp->fprintf(DESMUME_BACKUP_FOOTER_TXT);

	//and now the actual footer
	fp->write32le(addr);			//the size of data that has actually been written
	fp->write32le(padSize);			//the size we padded it to
	fp->write32le(info.type);		//save memory type
	fp->write32le(addr_size);
	fp->write32le(info.size);		//save memory size
	fp->write32le((u32)0);		//version number
	fp->fprintf("%s", kDesmumeSaveCookie); //this is what we'll use to recognize the desmume format save

	fp->fflush();

	//this is a HORRIBLE IDEA.
	//leave the FP positioned to write the final byte
	//this is a HACK to make the basic read/write byte operation work when it calls ensure().
	//IDEALLY, no assumptions about the file pointer can be made.
	//but someone (actually, not really) so very carefully profiled the save IO code and discovered that not fseeking for every byte read/write was a great optimization.
	//so, now all this code is depending/assuming on the FP being kept in a precise position, and I dont think its smart to change the main user of this assumption to paper over this bug by making it fseek before read/write, while leaving other unknown assuming clients intact
	fpMC->fseek(addr-1, SEEK_SET);
#endif
}
示例#5
0
	virtual void connect()
	{
		Close();
		romSize = 0;
		sramSize = 0;
		
		if (gameInfo.romsize == 0)
		{
			return;
		}
		
		if (GBACartridge_RomPath.empty())
		{
			return;
		}
		
		if (!strcasecmp(GBACartridge_RomPath.c_str(), "self"))
		{
			GBACartridge_RomPath = path.path;
			GBACartridge_SRAMPath = Path::GetFileNameWithoutExt(GBACartridge_RomPath) + "." + GBA_SRAM_FILE_EXT;
		}
		
		printf("GBASlot opening ROM: %s\n", GBACartridge_RomPath.c_str());
		EMUFILE_FILE *inf = new EMUFILE_FILE(GBACartridge_RomPath, "rb");
		inf->EnablePositionCache();
		fROM = inf;
		if (fROM->fail())
		{
			printf(" - Failed\n");
			Close();
			
			return;
		}
		
		romSize = fROM->size();
		printf(" - Success (%u bytes)\n", romSize);
		
		// Load the GBA cartridge SRAM.
		inf = new EMUFILE_FILE(GBACartridge_SRAMPath, "rb+");
		fSRAM = inf;
		if(fSRAM->fail())
		{
			delete fSRAM;
			fSRAM = NULL;
			printf("GBASlot did not load associated SRAM.\n");
		}
		else
		{
			inf->EnablePositionCache();
			sramSize = fSRAM->size();
			printf("Scanning GBA rom to ID save type\n");
			saveType = scanSaveTypeGBA();
			printf("\nGBASlot found SRAM (%s - %u bytes) at:\n%s\n", (saveType == 0xFF)?"Unknown":saveTypes[saveType], sramSize, GBACartridge_SRAMPath.c_str());
			gbaFlash.size = sramSize;
			if (gbaFlash.size <= (64 * 1024))
			{
				gbaFlash.idDevice = 0x1B;
				gbaFlash.idManufacturer = 0x32;
			}
			else
			{
				gbaFlash.idDevice = 0x09;
				gbaFlash.idManufacturer = 0xC2;
			}
			gbaFlash.state = 0;
		}
	}
示例#6
0
FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, const char * mode, const char *ext, int index, const char** extensions)
{
	FILE *ipsfile=0;
	FCEUFILE *fceufp=0;

	bool read = (std::string)mode == "rb";
	bool write = (std::string)mode == "wb";
	if((read&&write) || (!read&&!write))
	{
		FCEU_PrintError("invalid file open mode specified (only wb and rb are supported)");
		return 0;
	}

	std::string archive,fname,fileToOpen;
	FCEU_SplitArchiveFilename(path,archive,fname,fileToOpen);
	

	//try to setup the ips file
	if(ipsfn && read)
		ipsfile=fopen(ipsfn,"rb");
	if(read)
	{
		ArchiveScanRecord asr = FCEUD_ScanArchive(fileToOpen);
		asr.files.FilterByExtension(extensions);
		if(!asr.isArchive())
		{
			//if the archive contained no files, try to open it the old fashioned way
			EMUFILE_FILE* fp = new EMUFILE_FILE(fileToOpen,mode);
			if(!fp || (fp->get_fp() == NULL))
			{
				return 0;
			}

			//open a plain old file
			fceufp = new FCEUFILE();
			fceufp->filename = fileToOpen;
			fceufp->logicalPath = fileToOpen;
			fceufp->fullFilename = fileToOpen;
			fceufp->archiveIndex = -1;
			fceufp->stream = fp;
			FCEU_fseek(fceufp,0,SEEK_END);
			fceufp->size = fceufp->stream->ftell();
			FCEU_fseek(fceufp,0,SEEK_SET);
			goto applyips;
		}
		else
		{
			//open an archive file
			if(archive == "")
				if(index != -1)
					fceufp = FCEUD_OpenArchiveIndex(asr, fileToOpen, index);
				else 
					fceufp = FCEUD_OpenArchive(asr, fileToOpen, 0);
			else
				fceufp = FCEUD_OpenArchive(asr, archive, &fname);

			if(!fceufp) return 0;

			FileBaseInfo fbi = DetermineFileBase(fileToOpen.c_str());
			fceufp->logicalPath = fbi.filebasedirectory + fceufp->filename;
			goto applyips;
		}

applyips:
		return fceufp;
	}
	return 0;
}
示例#7
0
void BackupDevice::loadfile()
{
	//never use save files if we are in movie mode
	if(isMovieMode) return;
	if(filename.length() ==0) return; //No sense crashing if no filename supplied

	EMUFILE_FILE* inf = new EMUFILE_FILE(filename.c_str(),"rb");
	if(inf->fail())
	{
		delete inf;
		//no dsv found; we need to try auto-importing a file with .sav extension 
		printf("DeSmuME .dsv save file not found. Trying to load an old raw .sav file.\n");
		
		//change extension to sav
		char tmp[MAX_PATH];
		strcpy(tmp,filename.c_str());
		tmp[strlen(tmp)-3] = 0;
		strcat(tmp,"sav");

		inf = new EMUFILE_FILE(tmp,"rb");
		if(inf->fail())
		{
			delete inf;
			printf("Missing save file %s\n",filename.c_str());
			return;
		}
		delete inf;

		if (!load_no_gba(tmp))
			load_raw(tmp);
	}
	else
	{
		//scan for desmume save footer
		const s32 cookieLen = (s32)strlen(kDesmumeSaveCookie);
		char *sigbuf = new char[cookieLen];
		inf->fseek(-cookieLen, SEEK_END);
		inf->fread(sigbuf,cookieLen);
		int cmp = memcmp(sigbuf,kDesmumeSaveCookie,cookieLen);
		delete[] sigbuf;
		if(cmp)
		{
			//maybe it is a misnamed raw save file. try loading it that way
			printf("Not a DeSmuME .dsv save file. Trying to load as raw.\n");
			delete inf;
			if (!load_no_gba(filename.c_str()))
				load_raw(filename.c_str());
			return;
		}
		//desmume format
		inf->fseek(-cookieLen, SEEK_END);
		inf->fseek(-4, SEEK_CUR);
		u32 version = 0xFFFFFFFF;
		read32le(&version,inf);
		if(version!=0) {
			printf("Unknown save file format\n");
			return;
		}
		inf->fseek(-24, SEEK_CUR);
		read32le(&info.size,inf);
		read32le(&info.padSize,inf);
		read32le(&info.type,inf);
		read32le(&info.addr_size,inf);
		read32le(&info.mem_size,inf);

		u32 left = 0;
		if (CommonSettings.autodetectBackupMethod == 1)
		{
			if (advsc.isLoaded())
			{
				info.type = advsc.getSaveType();
				if (info.type != 0xFF || info.type != 0xFE)
				{
					u32 adv_size = save_types[info.type+1][1];
					if (info.size > adv_size)
						info.size = adv_size;
					else
						if (info.size < adv_size)
						{
							left = adv_size - info.size;
							info.size = adv_size;
						}
				}
			}
		}
		//establish the save data
		resize(info.size);
		inf->fseek(0, SEEK_SET);
		if(info.size>0)
			inf->fread(&data[0],info.size - left); //read all the raw data we have
		state = RUNNING;
		addr_size = info.addr_size;
		//none of the other fields are used right now


		if (CommonSettings.autodetectBackupMethod != 1 && info.type == 0) 
		{
			info.type = searchFileSaveType(info.size);
			if (info.type == 0xFF) info.type = 0;
		}
		u32 ss = info.size * 8 / 1024;
		if (ss >= 1024)
		{
			ss /= 1024;
			printf("Backup size: %i Mbit\n", ss);
		}
		else
			printf("Backup size: %i Kbit\n", ss);

		delete inf;
	}
}
示例#8
0
文件: mc.cpp 项目: Annovae/desmume
void BackupDevice::ensure(u32 addr, u8 val, EMUFILE_FILE *fpOut)
{
	if (!fpOut && (addr < fsize)) return;

	EMUFILE_FILE *fp = fpOut?fpOut:fpMC;

#ifndef _DONT_SAVE_BACKUP
	fp->fseek(fsize, SEEK_SET);
#endif
	
	u32 padSize = pad_up_size(addr);
	u32 size = padSize - fsize;
	info.padSize = info.size = fsize = padSize;
	int type = searchFileSaveType(fsize);
	if (type != 0xFF) info.type = (type + 1);

#ifndef _DONT_SAVE_BACKUP
	if (size > 0)
	{
		u8 *tmp = new u8[size];
		memset(tmp, val, size);
		fwrite(tmp, 1, size, fp->get_fp());
		delete [] tmp;
	}

	//this is just for humans to read
	fp->fprintf(DESMUME_BACKUP_FOOTER_TXT);

	//and now the actual footer
	fp->write32le(addr);			//the size of data that has actually been written
	fp->write32le(padSize);			//the size we padded it to
	fp->write32le(info.type);		//save memory type
	fp->write32le(addr_size);
	fp->write32le(info.size);		//save memory size
	fp->write32le((u32)0);		//version number
	fp->fprintf("%s", kDesmumeSaveCookie); //this is what we'll use to recognize the desmume format save

	fp->fflush();

	fp->fseek(addr, SEEK_SET);
#endif
}
示例#9
0
文件: mc.cpp 项目: Annovae/desmume
BackupDevice::BackupDevice()
{
	fpMC = NULL;
	fsize = 0;
	addr_size = 0;
	isMovieMode = false;

	if (gameInfo.romsize == 0) return;

	char buf[MAX_PATH] = {0};
	memset(buf, 0, MAX_PATH);
	path.getpathnoext(path.BATTERY, buf);
	filename = std::string(buf) + ".dsv";						// DeSmuME memory card

	MCLOG("MC: %s\n", filename.c_str());

	bool fexists = (access(filename.c_str(), 0) == 0)?true:false;

	if (fexists && CommonSettings.backupSave)
	{
		std::string tmp_fsav = std::string(buf) + ".dsv.bak";
		EMUFILE_FILE *in = new EMUFILE_FILE(filename, "rb");
		if (!in->fail())
		{
			u32 sz = in->size();
			if (sz > 0)
			{
				EMUFILE_FILE *out = new EMUFILE_FILE(tmp_fsav, "wb");
				if (!out->fail())
				{
					u8 *data = new u8[sz];
					fread(data, 1, sz, in->get_fp());
					fwrite(data, 1, sz, out->get_fp());
					delete [] data;
				}
				delete out;
			}
		}
		delete in;
	}

	if (!fexists)
	{
		printf("DeSmuME .dsv save file not found. Trying to load an old raw .sav file.\n");
		std::string tmp_fsav = std::string(buf) + ".sav";

		EMUFILE_FILE *fpTmp = new EMUFILE_FILE(tmp_fsav, "rb");
		if (!fpTmp->fail())
		{
			u32 sz = fpTmp->size();

			if (sz > 0)
			{
				EMUFILE_FILE *fpOut = new EMUFILE_FILE(filename, "wb");
				if (!fpOut->fail())
				{
					u8 *buf = new u8[sz + 1];
					if ((buf) && (fread(buf, 1, sz, fpTmp->get_fp()) == sz))
					{
						if (no_gba_unpack(buf, sz))
							printf("Converted from no$gba save.\n");
						else
							sz = trim(buf, sz);

						if (fwrite(buf, 1, sz, fpOut->get_fp()) == sz)
						{
							u8 res = searchFileSaveType(sz);
							if (res != 0xFF)
							{
								info.type = (res + 1);
								addr_size = info.addr_size = save_types[info.type].addr_size;
								info.size = fsize = sz;
								ensure(sz, fpOut);
								fsize = 0;
							}
							else
								info.type = 0;
							fexists = true;
						}
					}
					delete [] buf;
				}
				delete fpOut;
			}
		}
		delete fpTmp;
	}

	fpMC = new EMUFILE_FILE(filename, fexists?"rb+":"wb+");
	if (!fpMC->fail())
	{
		fsize = fpMC->size();
		if (fsize < saveSizes[0])
			fpMC->truncate(0);

		if (readFooter() == 0)
			fsize -= (strlen(kDesmumeSaveCookie) + strlen(DESMUME_BACKUP_FOOTER_TXT) + 24);
		else
		{
			memset(&info, 0, sizeof(info));
			fsize = 0;
		}
	
		fpMC->fseek(0, SEEK_SET);

		u32 left = 0;
		if (CommonSettings.autodetectBackupMethod == 1)
		{
			if (advsc.isLoaded())
			{
				info.type = advsc.getSaveType();
				if (info.type != 0xFF && info.type != 0xFE)
				{
					info.type++;
					u32 adv_size = save_types[info.type].size;
					if (info.size > adv_size)
					{
						info.size = adv_size;
						fpMC->truncate(adv_size);
						ensure(adv_size, fpMC);
					}
					else
						if (info.size < adv_size)
						{
							left = adv_size - info.size;
							info.size = adv_size;
							ensure(adv_size);
						}

					fsize = adv_size;
				}
			}
		}

		addr_size = info.addr_size;
		info.padSize = fsize;
		//none of the other fields are used right now

		if (CommonSettings.autodetectBackupMethod != 1 && info.type == 0) 
		{
			info.type = searchFileSaveType(info.size);
			if (info.type == 0xFF) info.type = 0;
		}

		u32 ss = (info.padSize * 8) / 1024;
		bool _Mbit = false;

		if (ss >= 1024)
		{
			ss /= 1024;
			_Mbit = true;
		}

		if (ss > 0)
			printf("Backup size: %u %cbit\n", ss, _Mbit?'M':'K');
	}

	state = (fsize > 0)?RUNNING:DETECTING;
	reset();
}