bool savestate_save(EMUFILE* outstream) { #ifdef HAVE_JIT arm_jit_sync(); #endif EMUFILE_MEMORY ms; EMUFILE* os = outstream; os->fseek(32,SEEK_SET); //skip the header writechunks(os); //save the length of the file u32 len = os->ftell(); u32 comprlen = 0xFFFFFFFF; u8* cbuf; //dump the header outstream->fseek(0,SEEK_SET); outstream->fwrite(magic,16); write32le(SAVESTATE_VERSION,outstream); write32le(EMU_DESMUME_VERSION_NUMERIC(),outstream); //desmume version write32le(len,outstream); //uncompressed length write32le(comprlen,outstream); //compressed length (-1 if it is not compressed) return true; }
void HISTORY::save(EMUFILE *os, bool really_save) { if (really_save) { int real_pos, last_tick = 0; // write "HISTORY" string os->fwrite(historySaveID, HISTORY_ID_LEN); // write vars write32le(historyCursorPos, os); write32le(historyTotalItems, os); // write items starting from history_start_pos for (int i = 0; i < historyTotalItems; ++i) { real_pos = (historyStartPos + i) % historySize; snapshots[real_pos].save(os); bookmarkBackups[real_pos].save(os); os->fwrite(¤tBranchNumberBackups[real_pos], 1); if (i / SAVING_HISTORY_PROGRESSBAR_UPDATE_RATE > last_tick) { playback.setProgressbar(i, historyTotalItems); last_tick = i / PROGRESSBAR_UPDATE_RATE; } } } else { // write "HISTORX" string os->fwrite(historySkipSaveID, HISTORY_ID_LEN); } }
static int savestate_WriteChunk(EMUFILE* os, int type, const SFORMAT *sf) { write32le(type,os); if(!sf) return 4; int bsize = SubWrite((EMUFILE*)0,sf); write32le(bsize,os); if(!SubWrite(os,sf)) { return 8; } return (bsize+8); }
bool BackupDevice::save_state(EMUFILE* os) { u32 savePos = fpMC->ftell(); std::vector<u8> data(fsize); fpMC->fseek(0, SEEK_SET); fread((char*)&data[0], 1, fsize, fpMC->get_fp()); u32 version = 5; //v0 write32le(version,os); write32le(write_enable,os); write32le(com,os); write32le(addr_size,os); write32le(addr_counter,os); write32le((u32)state,os); writebuffer(data,os); writebuffer(data_autodetect,os); //v1 write32le(addr,os); //v2 write8le(motionInitState,os); write8le(motionFlag,os); //v3 writebool(reset_command_state,os); //v4 write8le(write_protect,os); //v5 write32le(savePos,os); fpMC->fseek(savePos, SEEK_SET); data.clear(); return true; }
int writebuffer(std::vector<u8>& vec, EMUFILE* os) { u32 size = vec.size(); write32le(size,os); if(size>0) os->fwrite((char*)&vec[0],size); return 1; }
void writeTo(uint8_t *Buf) override { uint8_t *P = Buf + FileOff; for (Chunk *C : Chunks) { write32le(P, C->getRVA()); P += 4; } }
void write32(bool be,void *p,uint32_t d) { if (be) write32be(p,d); else write32le(p,d); }
static void mmu_savestate(EMUFILE* os) { u32 version = 8; write32le(version,os); //version 2: MMU_new.backupDevice.save_state(os); //version 3: MMU_new.gxstat.savestate(os); for(int i=0;i<2;i++) for(int j=0;j<4;j++) MMU_new.dma[i][j].savestate(os); MMU_timing.arm9codeFetch.savestate(os, version); MMU_timing.arm9dataFetch.savestate(os, version); MMU_timing.arm7codeFetch.savestate(os, version); MMU_timing.arm7dataFetch.savestate(os, version); MMU_timing.arm9codeCache.savestate(os, version); MMU_timing.arm9dataCache.savestate(os, version); //version 4: MMU_new.sqrt.savestate(os); MMU_new.div.savestate(os); //version 6: MMU_new.dsi_tsc.save_state(os); //version 8: os->write32le(MMU.fw.size); os->fwrite(MMU.fw.data,MMU.fw.size); }
int FCEUSS_SaveFP(MEM_TYPE *st) { static uint32 totalsize; uint8 header[16] = { 0 }; header[0] = 'F'; header[1] = 'C'; header[2] = 'S'; header[3] = 0xFF; header[3] = 0xFF; FCEU_en32lsb(header + 8, FCEU_VERSION_NUMERIC); fwrite(header, 1, 16, st); FCEUPPU_SaveState(); FCEUSND_SaveState(); totalsize = WriteStateChunk(st, 1, SFCPU); totalsize += WriteStateChunk(st, 2, SFCPUC); totalsize += WriteStateChunk(st, 3, FCEUPPU_STATEINFO); totalsize += WriteStateChunk(st, 4, FCEUCTRL_STATEINFO); totalsize += WriteStateChunk(st, 5, FCEUSND_STATEINFO); if (SPreSave) SPreSave(); totalsize += WriteStateChunk(st, 0x10, SFMDATA); if (SPreSave) SPostSave(); fseek(st, 4, SEEK_SET); write32le(totalsize, st); return(1); }
void writeTo(uint8_t *Buf) override { // An import-by-ordinal slot has MSB 1 to indicate that // this is import-by-ordinal (and not import-by-name). if (Config->is64()) { write64le(Buf + FileOff, (1ULL << 63) | Ordinal); } else { write32le(Buf + FileOff, (1ULL << 31) | Ordinal); } }
static void cp15_savestate(EMUFILE* os) { //version write32le(1,os); cp15.saveone(os); //ARM7 not have coprocessor //cp15_saveone((armcp15_t *)NDS_ARM7.coproc[15],os); }
void EMUFILE::writeMemoryStream(EMUFILE_MEMORY* ms) { s32 size = (s32)ms->size(); write32le(size); if(size>0) { std::vector<u8>* vec = ms->get_vec(); fwrite(&vec->at(0),size); } }
static int WriteStateChunk(MEM_TYPE *st, int type, SFORMAT *sf) { int bsize; fputc(type, st); bsize = SubWrite(0, sf); write32le(bsize, st); if (!SubWrite(st, sf)) return(0); return(bsize + 5); }
static void savestate_WriteChunk(EMUFILE* os, int type, void (*saveproc)(EMUFILE* os)) { u32 pos1 = os->ftell(); //write the type, size(placeholder), and data write32le(type,os); os->fseek(4, SEEK_CUR); // skip the size, we write that later saveproc(os); //get the size u32 pos2 = os->ftell(); assert(pos2 != (u32)-1); // if this assert fails, saveproc did something bad u32 size = (pos2 - pos1) - (2 * sizeof(u32)); //fill in the actual size os->fseek(pos1 + sizeof(u32),SEEK_SET); write32le(size,os); os->fseek(pos2,SEEK_SET); /* // old version of this function, // for reference in case the new one above starts misbehaving somehow: // - this is retarded. why not write placeholders for size and then write directly to the stream //and then go back and fill them in //get the size memorystream mstemp; saveproc(&mstemp); mstemp.flush(); u32 size = mstemp.size(); //write the type, size, and data write32le(type,os); write32le(size,os); os->write(mstemp.buf(),size); */ }
bool BackupDevice::save_state(std::ostream* os) { int version = 1; write32le(version,os); write32le(write_enable,os); write32le(com,os); write32le(addr_size,os); write32le(addr_counter,os); write32le((u32)state,os); writebuffer(data,os); writebuffer(data_autodetect,os); write32le(addr,os); return true; }
bool BackupDevice::save_state(EMUFILE* os) { size_t elements_read; u32 version = 5; u32 savePos = fpMC->ftell(); std::vector<u8> data(fsize); fpMC->fseek(0, SEEK_SET); if (data.size() != 0) elements_read = fread(&data[0], 1, fsize, fpMC->get_fp()); if (elements_read != fsize) printf( "Expected %u bytes from saved state but read %lu.\n", fsize, elements_read ); //v0 write32le(version,os); write32le(write_enable,os); write32le(com,os); write32le(addr_size,os); write32le(addr_counter,os); write32le((u32)state,os); writebuffer(data,os); writebuffer(data_autodetect,os); //v1 write32le(addr,os); //v2 write8le(motionInitState,os); write8le(motionFlag,os); //v3 writebool(reset_command_state,os); //v4 write8le(write_protect,os); //v5 write32le(savePos,os); fpMC->fseek(savePos, SEEK_SET); data.clear(); return true; }
static int SubWrite(FILE *st, SFORMAT *sf) { uint32 acc=0; while(sf->v) { if(sf->s==~0) /* Link to another struct. */ { uint32 tmp; if(!(tmp=SubWrite(st,(SFORMAT *)sf->v))) return(0); acc+=tmp; sf++; continue; } acc+=8; /* Description + size */ acc+=sf->s&(~RLSB); if(st) /* Are we writing or calculating the size of this block? */ { fwrite(sf->desc,1,4,st); write32le(sf->s&(~RLSB),st); #ifndef LSB_FIRST if(sf->s&RLSB) FlipByteOrder(sf->v,sf->s&(~RLSB)); #endif fwrite((uint8 *)sf->v,1,sf->s&(~RLSB),st); /* Now restore the original byte order. */ #ifndef LSB_FIRST if(sf->s&RLSB) FlipByteOrder(sf->v,sf->s&(~RLSB)); #endif } sf++; } return(acc); }
bool BackupDevice::save_state(EMUFILE* os) { u32 version = 2; //v0 write32le(version,os); write32le(write_enable,os); write32le(com,os); write32le(addr_size,os); write32le(addr_counter,os); write32le((u32)state,os); writebuffer(data,os); writebuffer(data_autodetect,os); //v1 write32le(addr,os); //v2 write8le(motionInitState,os); write8le(motionFlag,os); return true; }
int FCEUSS_SaveFP(FILE *st) { static uint32 totalsize; static uint8 header[16]="FCS"; memset(header+4,0,13); header[3]=0xFF; FCEU_en32lsb(header + 8, FCEU_VERSION_NUMERIC); fwrite(header,1,16,st); FCEUPPU_SaveState(); FCEUSND_SaveState(); totalsize=WriteStateChunk(st,1,SFCPU); totalsize+=WriteStateChunk(st,2,SFCPUC); totalsize+=WriteStateChunk(st,3,FCEUPPU_STATEINFO); totalsize+=WriteStateChunk(st,4,FCEUCTRL_STATEINFO); totalsize+=WriteStateChunk(st,5,FCEUSND_STATEINFO); if(SPreSave) SPreSave(); totalsize+=WriteStateChunk(st,0x10,SFMDATA); if(SPreSave) SPostSave(); fseek(st,4,SEEK_SET); write32le(totalsize,st); return(1); }
void BackupDevice::flush() { //never use save files if we are in movie mode if(isMovieMode) return; if (filename.length() == 0) return; EMUFILE* outf = new EMUFILE_FILE(filename.c_str(),"wb"); if(!outf->fail()) { if(data.size()>0) outf->fwrite(&data[0],data.size()); //write the footer. we use a footer so that we can maximize the chance of the //save file being recognized as a raw save file by other emulators etc. //first, pad up to the next largest known save size. u32 size = data.size(); u32 padSize = pad_up_size(size); for(u32 i=size;i<padSize;i++) outf->fputc(kUninitializedSaveDataValue); //this is just for humans to read outf->fprintf("|<--Snip above here to create a raw sav by excluding this DeSmuME savedata footer:"); //and now the actual footer write32le(size,outf); //the size of data that has actually been written write32le(padSize,outf); //the size we padded it to write32le(info.type,outf); //save memory type write32le(addr_size,outf); write32le(info.size,outf); //save memory size write32le(0,outf); //version number outf->fprintf("%s", kDesmumeSaveCookie); //this is what we'll use to recognize the desmume format save delete outf; } else { delete outf; printf("Unable to open savefile %s\n", filename.c_str()); } }
void SNAPSHOT::save(EMUFILE *os) { // write vars write32le(keyFrame, os); write32le(startFrame, os); write32le(endFrame, os); write32le(consecutivenessTag, os); write32le(recordedJoypadDifferenceBits, os); write32le(modificationType, os); // write description int len = strlen(description); write8le(len, os); os->fwrite(&description[0], len); // save InputLog data inputlog.save(os); // save LagLog data laglog.save(os); // save Markers data markers.save(os); }
void GREENZONE::save(EMUFILE *os, int save_type) { if (save_type != GREENZONE_SAVING_MODE_NO) { collectCurrentState(); // in case the project is being saved before the greenzone.update() was called within current frame runGreenzoneCleaning(); if (greenzoneSize > (int)savestates.size()) greenzoneSize = savestates.size(); // write "GREENZONE" string os->fwrite(greenzone_save_id, GREENZONE_ID_LEN); // write LagLog lagLog.save(os); // write size write32le(greenzoneSize, os); // write Playback cursor position write32le(currFrameCounter, os); } int frame, size; int last_tick = 0; switch (save_type) { case GREENZONE_SAVING_MODE_ALL: { // write savestates for (frame = 0; frame < greenzoneSize; ++frame) { // update TASEditor progressbar from time to time if (frame / PROGRESSBAR_UPDATE_RATE > last_tick) { playback.setProgressbar(frame, greenzoneSize); last_tick = frame / PROGRESSBAR_UPDATE_RATE; } if (!savestates[frame].size()) continue; write32le(frame, os); // write savestate size = savestates[frame].size(); write32le(size, os); os->fwrite(&savestates[frame][0], size); } // write -1 as eof for greenzone write32le(-1, os); break; } case GREENZONE_SAVING_MODE_16TH: { // write savestates for (frame = 0; frame < greenzoneSize; ++frame) { if (!(frame & 0xF) || frame == currFrameCounter) { // update TASEditor progressbar from time to time if (frame / PROGRESSBAR_UPDATE_RATE > last_tick) { playback.setProgressbar(frame, greenzoneSize); last_tick = frame / PROGRESSBAR_UPDATE_RATE; } if (!savestates[frame].size()) continue; write32le(frame, os); // write savestate size = savestates[frame].size(); write32le(size, os); os->fwrite(&savestates[frame][0], size); } } // write -1 as eof for greenzone write32le(-1, os); break; } case GREENZONE_SAVING_MODE_MARKED: { // write savestates for (frame = 0; frame < greenzoneSize; ++frame) { if (markersManager.getMarkerAtFrame(frame) || frame == currFrameCounter) { // update TASEditor progressbar from time to time if (frame / PROGRESSBAR_UPDATE_RATE > last_tick) { playback.setProgressbar(frame, greenzoneSize); last_tick = frame / PROGRESSBAR_UPDATE_RATE; } if (!savestates[frame].size()) continue; write32le(frame, os); // write savestate size = savestates[frame].size(); write32le(size, os); os->fwrite(&savestates[frame][0], size); } } // write -1 as eof for greenzone write32le(-1, os); break; } case GREENZONE_SAVING_MODE_NO: { // write "GREENZONX" string os->fwrite(greenzone_skipsave_id, GREENZONE_ID_LEN); // write LagLog lagLog.save(os); // write Playback cursor position write32le(currFrameCounter, os); if (currFrameCounter > 0) { // write ONE savestate for currFrameCounter collectCurrentState(); int size = savestates[currFrameCounter].size(); write32le(size, os); os->fwrite(&savestates[currFrameCounter][0], size); } break; } } }
static void FlushHeader(void) { if(current <= 0) return;// only write header data if recording FILE* fp = slots[current - 1]; if(fp == 0) return; unsigned long loc = ftell(fp); fseek(fp, 4, SEEK_SET); write32le(MOVIE_VERSION, fp); fseek(fp, 12, SEEK_SET); write32le(framecount, fp); write32le(rerecord_count, fp); write32le(frameptr, fp); fseek(fp, 32, SEEK_SET); fwrite(FCEUGameInfo->MD5, 1, 16, fp); // write ROM checksum write32le(FCEU_VERSION_NUMERIC, fp); // write emu version used // write ROM name used fseek(fp, 52, SEEK_SET); char str[512]; fgets(str,512,fp); str[511]='\0'; int strdiff=strlen(FileBase)-strlen(str); if(strdiff) { // resize the whole damn movie because the ROM name in the header is of variable length int off=52; fseek(fp, 52, SEEK_SET); do { off++; } while(fgetc(fp) && !feof(fp) && !ferror(fp)); if(feof(fp) || ferror(fp)) { fseek(fp, loc, SEEK_SET); return; } fseek(fp, 0, SEEK_END); uint32 fsize=ftell(fp)-off; char* ctemp=(char*)FCEU_malloc(fsize*sizeof(char)+4); if(!ctemp) { fseek(fp, loc, SEEK_SET); return; } fseek(fp, off, SEEK_SET); fread(ctemp, 1,fsize, fp); fseek(fp, 52+strlen(FileBase)+1, SEEK_SET); int wrote = fwrite(ctemp, fsize,1, fp); FCEU_free(ctemp); if(!wrote) { fseek(fp, loc, SEEK_SET); return; } if(loc >= firstframeoffset) loc += strdiff; savestate_offset += strdiff; firstframeoffset += strdiff; fseek(fp, 24, SEEK_SET); write32le(savestate_offset, fp); write32le(firstframeoffset, fp); } fseek(fp, 52, SEEK_SET); fputs(FileBase, fp); fputc('\0', fp); fseek(fp, loc, SEEK_SET); }
static int SubWrite(EMUFILE* os, const SFORMAT *sf) { uint32 acc=0; #ifdef DEBUG std::set<std::string> keyset; #endif const SFORMAT* temp = sf; while(temp->v) { const SFORMAT* seek = sf; while(seek->v && seek != temp) { if(!strcmp(seek->desc,temp->desc)) { printf("ERROR! duplicated chunk name: %s\n", temp->desc); } seek++; } temp++; } while(sf->v) { //not supported right now //if(sf->size==~0) //Link to another struct //{ // uint32 tmp; // if(!(tmp=SubWrite(os,(SFORMAT *)sf->v))) // return(0); // acc+=tmp; // sf++; // continue; //} int count = sf->count; int size = sf->size; //add size of current node to the accumulator acc += 4 + sizeof(sf->size) + sizeof(sf->count); acc += count * size; if(os) //Are we writing or calculating the size of this block? { os->fwrite(sf->desc,4); write32le(sf->size,os); write32le(sf->count,os); #ifdef DEBUG //make sure we dont dup any keys if(keyset.find(sf->desc) != keyset.end()) { printf("duplicate save key!\n"); assert(false); } keyset.insert(sf->desc); #endif #ifdef MSB_FIRST if(size == 1) { //special case: write a huge byte array os->fwrite((char *)sf->v,count); } else { for(int i=0;i<count;i++) { FlipByteOrder((u8*)sf->v + i*size, size); os->fwrite((char*)sf->v + i*size,size); //Now restore the original byte order. FlipByteOrder((u8*)sf->v + i*size, size); } } #else // no need to ever loop one at a time if not flipping byte order os->fwrite((char *)sf->v,size*count); #endif } sf++; } return(acc); }
void EMUFILE::write32le(u32* val) { write32le(*val); }
// Save state void armcp15_t::saveone(EMUFILE* os) { write32le(IDCode,os); write32le(cacheType,os); write32le(TCMSize,os); write32le(ctrl,os); write32le(DCConfig,os); write32le(ICConfig,os); write32le(writeBuffCtrl,os); write32le(und,os); write32le(DaccessPerm,os); write32le(IaccessPerm,os); for(int i=0;i<8;i++) write32le(protectBaseSize[i],os); write32le(cacheOp,os); write32le(DcacheLock,os); write32le(IcacheLock,os); write32le(ITCMRegion,os); write32le(DTCMRegion,os); write32le(processID,os); write32le(RAM_TAG,os); write32le(testState,os); write32le(cacheDbg,os); for(int i=0;i<8;i++) write32le(regionWriteMask_USR[i],os); for(int i=0;i<8;i++) write32le(regionWriteMask_SYS[i],os); for(int i=0;i<8;i++) write32le(regionReadMask_USR[i],os); for(int i=0;i<8;i++) write32le(regionReadMask_SYS[i],os); for(int i=0;i<8;i++) write32le(regionExecuteMask_USR[i],os); for(int i=0;i<8;i++) write32le(regionExecuteMask_SYS[i],os); for(int i=0;i<8;i++) write32le(regionWriteSet_USR[i],os); for(int i=0;i<8;i++) write32le(regionWriteSet_SYS[i],os); for(int i=0;i<8;i++) write32le(regionReadSet_USR[i],os); for(int i=0;i<8;i++) write32le(regionReadSet_SYS[i],os); for(int i=0;i<8;i++) write32le(regionExecuteSet_USR[i],os); for(int i=0;i<8;i++) write32le(regionExecuteSet_SYS[i],os); }
void FCEUI_SaveMovie(char *fname, uint8 flags, const char* metadata) { FILE *fp; char *fn; int poweron=0; uint8 padding[4] = {0,0,0,0}; int n_padding; FCEUI_StopMovie(); char origname[512]; if(fname) { fp = FCEUD_UTF8fopen(fname, "wb"); strcpy(origname,fname); } else { fp=FCEUD_UTF8fopen(fn=FCEU_MakeFName(FCEUMKF_MOVIE,CurrentMovie,0),"wb"); strcpy(origname,fn); free(fn); } if(!fp) return; // don't need the movieSyncHackOn sync hack for newly recorded movies flags |= MOVIE_FLAG_NOSYNCHACK; resetDMCacc=movieSyncHackOn=0; // add PAL flag if(FCEUI_GetCurrentVidSystem(0,0)) flags |= MOVIE_FLAG_PAL; if(flags & MOVIE_FLAG_FROM_POWERON) { poweron=1; flags &= ~MOVIE_FLAG_FROM_POWERON; flags |= MOVIE_FLAG_FROM_RESET; } // write header write32le(MOVIE_MAGIC, fp); write32le(MOVIE_VERSION, fp); fputc(flags, fp); fputc(0, fp); // reserved fputc(0, fp); // reserved fputc(0, fp); // reserved write32le(0, fp); // leave room for length frames write32le(0, fp); // leave room for rerecord count write32le(0, fp); // leave room for movie data size write32le(0, fp); // leave room for savestate_offset write32le(0, fp); // leave room for offset_to_controller_data fwrite(FCEUGameInfo->MD5, 1, 16, fp); // write ROM checksum write32le(FCEU_VERSION_NUMERIC, fp); // write emu version used fputs(FileBase, fp); // write ROM name used fputc(0, fp); if(metadata) { if(strlen(metadata) < MOVIE_MAX_METADATA) fputs(metadata, fp); else fwrite(metadata, 1, MOVIE_MAX_METADATA-1, fp); } fputc(0, fp); // add padding n_padding = (4 - (ftell(fp) & 0x3)) & 0x3; fwrite(padding, 1, n_padding, fp); if(flags & MOVIE_FLAG_FROM_RESET) { if(poweron) { // make a for-movie-recording power-on clear the game's save data, too // (note: FCEU makes a save state immediately after this and that loads that on movie playback) extern char lastLoadedGameName [2048]; extern int disableBatteryLoading, suppressAddPowerCommand; suppressAddPowerCommand=1; disableBatteryLoading=1; suppressMovieStop=1; { // NOTE: this will NOT write an FCEUNPCMD_POWER into the movie file FCEUGI * gi = FCEUI_LoadGame(lastLoadedGameName); if(!gi) PowerNES(); // and neither will this, if it can even happen } suppressMovieStop=0; disableBatteryLoading=0; suppressAddPowerCommand=0; } } savestate_offset = ftell(fp); FCEUSS_SaveFP(fp); fseek(fp, 0, SEEK_END); ResetInputTypes(); // add padding n_padding = (4 - (ftell(fp) & 0x3)) & 0x3; fwrite(padding, 1, n_padding, fp); firstframeoffset = ftell(fp); // finish header fseek(fp, 24, SEEK_SET); // offset_to_savestate offset write32le(savestate_offset, fp); write32le(firstframeoffset, fp); fseek(fp, firstframeoffset, SEEK_SET); // set recording flag current=CurrentMovie; movie_readonly = 0; frameptr = 0; framecount = 0; rerecord_count = 0; slots[current] = fp; memset(joop,0,sizeof(joop)); current++; framets=0; nextd = -1; // trigger a reset if(flags & MOVIE_FLAG_FROM_RESET) { if(poweron) { PowerNES(); // NOTE: this will write an FCEUNPCMD_POWER into the movie file } else ResetNES(); // NOTE: this will write an FCEUNPCMD_RESET into the movie file } if(!fname) FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */ else FCEU_DispMessage("Movie recording started."); strcpy(curMovieFilename, origname); }
void writeTo(uint8_t *Buf) override { write32le(Buf + FileOff, HintName->getRVA()); }
void mic_savestate(EMUFILE* os) { write32le(-1,os); }
void writebool(bool b, EMUFILE* os) { write32le(b?1:0,os); }