123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118 |
- /* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2021 - Daniel De Matteis
- * Copyright (C) 2012-2015 - Michael Lelli
- * Copyright (C) 2014-2017 - Jean-Andr� Santoni
- * Copyright (C) 2016-2019 - Brad Parker
- * Copyright (C) 2016-2019 - Andr�s Su�rez (input mapper code)
- * Copyright (C) 2016-2017 - Gregor Richards (network code)
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see <http://www.gnu.org/licenses/>.
- */
- #include <stdbool.h>
- #ifdef _WIN32
- #ifdef _XBOX
- #include <xtl.h>
- #else
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #endif
- #if defined(DEBUG) && defined(HAVE_DRMINGW)
- #include "exchndl.h"
- #endif
- #endif
- #if defined(DINGUX)
- #include <sys/types.h>
- #include <unistd.h>
- #endif
- #if (defined(__linux__) || defined(__unix__) || defined(DINGUX)) && !defined(EMSCRIPTEN)
- #include <signal.h>
- #endif
- #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
- #ifndef LEGACY_WIN32
- #define LEGACY_WIN32
- #endif
- #endif
- #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- #include <objbase.h>
- #include <process.h>
- #endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <stdint.h>
- #include <string.h>
- #include <ctype.h>
- #include <math.h>
- #include <locale.h>
- #include <boolean.h>
- #include <clamping.h>
- #include <string/stdstring.h>
- #include <dynamic/dylib.h>
- #include <file/config_file.h>
- #include <lists/string_list.h>
- #include <memalign.h>
- #include <retro_math.h>
- #include <retro_timers.h>
- #include <encodings/utf.h>
- #include <libretro.h>
- #ifdef HAVE_VULKAN
- #include <libretro_vulkan.h>
- #endif
- #define VFS_FRONTEND
- #include <vfs/vfs_implementation.h>
- #include <features/features_cpu.h>
- #include <compat/strl.h>
- #include <compat/strcasestr.h>
- #include <compat/getopt.h>
- #include <compat/posix_string.h>
- #include <streams/file_stream.h>
- #include <file/file_path.h>
- #include <retro_miscellaneous.h>
- #include <queues/message_queue.h>
- #include <lists/dir_list.h>
- #ifdef EMSCRIPTEN
- #include <emscripten/emscripten.h>
- #endif
- #ifdef HAVE_LIBNX
- #include <switch.h>
- #endif
- #if defined(HAVE_LAKKA) || defined(HAVE_LIBNX)
- #include "switch_performance_profiles.h"
- #endif
- #if defined(ANDROID)
- #include "play_feature_delivery/play_feature_delivery.h"
- #endif
- #ifdef HAVE_PRESENCE
- #include "network/presence.h"
- #endif
- #ifdef HAVE_DISCORD
- #include "network/discord.h"
- #endif
- #include "config.def.h"
- #include "runtime_file.h"
- #include "runloop.h"
- #include "camera/camera_driver.h"
- #include "location_driver.h"
- #include "record/record_driver.h"
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #ifdef HAVE_NETWORKING
- #include <net/net_compat.h>
- #include <net/net_socket.h>
- #endif
- #include <audio/audio_resampler.h>
- #include "audio/audio_driver.h"
- #include "gfx/gfx_animation.h"
- #include "gfx/gfx_display.h"
- #include "gfx/gfx_thumbnail.h"
- #include "gfx/video_filter.h"
- #include "input/input_osk.h"
- #ifdef HAVE_RUNAHEAD
- #include "runahead.h"
- #endif
- #ifdef HAVE_MENU
- #include "menu/menu_cbs.h"
- #include "menu/menu_driver.h"
- #include "menu/menu_input.h"
- #include "menu/menu_input_bind_dialog.h"
- #endif
- #ifdef HAVE_MENU
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- #include "menu/menu_shader.h"
- #endif
- #endif
- #ifdef HAVE_GFX_WIDGETS
- #include "gfx/gfx_widgets.h"
- #endif
- #include "input/input_keymaps.h"
- #include "input/input_remapping.h"
- #ifdef HAVE_CHEEVOS
- #include "cheevos/cheevos.h"
- #include "cheevos/cheevos_menu.h"
- #endif
- #ifdef HAVE_NETWORKING
- #include "network/netplay/netplay.h"
- #include "network/netplay/netplay_private.h"
- #ifdef HAVE_WIFI
- #include "network/wifi_driver.h"
- #endif
- #endif
- #ifdef HAVE_THREADS
- #include <rthreads/rthreads.h>
- #endif
- #include "autosave.h"
- #include "command.h"
- #include "config.features.h"
- #include "cores/internal_cores.h"
- #include "content.h"
- #include "core_info.h"
- #include "dynamic.h"
- #include "defaults.h"
- #include "msg_hash.h"
- #include "paths.h"
- #include "file_path_special.h"
- #include "ui/ui_companion_driver.h"
- #include "verbosity.h"
- #include "frontend/frontend_driver.h"
- #ifdef HAVE_THREADS
- #include "gfx/video_thread_wrapper.h"
- #endif
- #include "gfx/video_display_server.h"
- #ifdef HAVE_CRTSWITCHRES
- #include "gfx/video_crt_switch.h"
- #endif
- #ifdef HAVE_BLUETOOTH
- #include "bluetooth/bluetooth_driver.h"
- #endif
- #include "misc/cpufreq/cpufreq.h"
- #include "led/led_driver.h"
- #include "midi_driver.h"
- #include "location_driver.h"
- #include "core.h"
- #include "configuration.h"
- #include "list_special.h"
- #include "core_option_manager.h"
- #ifdef HAVE_CHEATS
- #include "cheat_manager.h"
- #endif
- #ifdef HAVE_REWIND
- #include "state_manager.h"
- #endif
- #include "tasks/task_content.h"
- #include "tasks/task_file_transfer.h"
- #include "tasks/task_powerstate.h"
- #include "tasks/tasks_internal.h"
- #include "performance_counters.h"
- #include "version.h"
- #include "version_git.h"
- #include "retroarch.h"
- #include "accessibility.h"
- #if defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX)
- #include "SDL.h"
- #endif
- #ifdef HAVE_LAKKA
- #include "lakka.h"
- #endif
- #if defined(HAVE_COCOATOUCH) && TARGET_OS_IOS
- #include "JITSupport.h"
- #endif
- #define SHADER_FILE_WATCH_DELAY_MSEC 500
- #define QUIT_DELAY_USEC 3 * 1000000 /* 3 seconds */
- #define DEFAULT_NETWORK_GAMEPAD_PORT 55400
- #define UDP_FRAME_PACKETS 16
- #ifdef HAVE_ZLIB
- #define DEFAULT_EXT "zip"
- #else
- #define DEFAULT_EXT ""
- #endif
- #ifdef HAVE_DYNAMIC
- #define SYMBOL(x) do { \
- function_t func = dylib_proc(lib_handle_local, #x); \
- memcpy(¤t_core->x, &func, sizeof(func)); \
- if (!current_core->x) { RARCH_ERR("Failed to load symbol: \"%s\"\n", #x); retroarch_fail(1, "runloop_init_libretro_symbols()"); } \
- } while (0)
- #else
- #define SYMBOL(x) current_core->x = x
- #endif
- #define SYMBOL_DUMMY(x) current_core->x = libretro_dummy_##x
- #ifdef HAVE_FFMPEG
- #define SYMBOL_FFMPEG(x) current_core->x = libretro_ffmpeg_##x
- #endif
- #ifdef HAVE_MPV
- #define SYMBOL_MPV(x) current_core->x = libretro_mpv_##x
- #endif
- #ifdef HAVE_IMAGEVIEWER
- #define SYMBOL_IMAGEVIEWER(x) current_core->x = libretro_imageviewer_##x
- #endif
- #if defined(HAVE_NETWORKING) && defined(HAVE_NETWORKGAMEPAD)
- #define SYMBOL_NETRETROPAD(x) current_core->x = libretro_netretropad_##x
- #endif
- #if defined(HAVE_VIDEOPROCESSOR)
- #define SYMBOL_VIDEOPROCESSOR(x) current_core->x = libretro_videoprocessor_##x
- #endif
- #define CORE_SYMBOLS(x) \
- x(retro_init); \
- x(retro_deinit); \
- x(retro_api_version); \
- x(retro_get_system_info); \
- x(retro_get_system_av_info); \
- x(retro_set_environment); \
- x(retro_set_video_refresh); \
- x(retro_set_audio_sample); \
- x(retro_set_audio_sample_batch); \
- x(retro_set_input_poll); \
- x(retro_set_input_state); \
- x(retro_set_controller_port_device); \
- x(retro_reset); \
- x(retro_run); \
- x(retro_serialize_size); \
- x(retro_serialize); \
- x(retro_unserialize); \
- x(retro_cheat_reset); \
- x(retro_cheat_set); \
- x(retro_load_game); \
- x(retro_load_game_special); \
- x(retro_unload_game); \
- x(retro_get_region); \
- x(retro_get_memory_data); \
- x(retro_get_memory_size);
- #ifdef _WIN32
- #define PERF_LOG_FMT "[PERF]: Avg (%s): %I64u ticks, %I64u runs.\n"
- #else
- #define PERF_LOG_FMT "[PERF]: Avg (%s): %llu ticks, %llu runs.\n"
- #endif
- static runloop_state_t runloop_state = {};
- /* GLOBAL POINTER GETTERS */
- runloop_state_t *runloop_state_get_ptr(void)
- {
- return &runloop_state;
- }
- #ifdef HAVE_REWIND
- bool state_manager_frame_is_reversed(void)
- {
- return (runloop_state.rewind_st.flags & STATE_MGR_REWIND_ST_FLAG_FRAME_IS_REVERSED) > 0;
- }
- #endif
- content_state_t *content_state_get_ptr(void)
- {
- return &runloop_state.content_st;
- }
- /* Get the current subsystem rom id */
- unsigned content_get_subsystem_rom_id(void)
- {
- return runloop_state.content_st.pending_subsystem_rom_id;
- }
- /* Get the current subsystem */
- int content_get_subsystem(void)
- {
- return runloop_state.content_st.pending_subsystem_id;
- }
- struct retro_perf_counter **retro_get_perf_counter_libretro(void)
- {
- return runloop_state.perf_counters_libretro;
- }
- unsigned retro_get_perf_count_libretro(void)
- {
- return runloop_state.perf_ptr_libretro;
- }
- void runloop_performance_counter_register(struct retro_perf_counter *perf)
- {
- if ( perf->registered
- || runloop_state.perf_ptr_libretro >= MAX_COUNTERS)
- return;
- runloop_state.perf_counters_libretro[runloop_state.perf_ptr_libretro++] = perf;
- perf->registered = true;
- }
- void runloop_log_counters(
- struct retro_perf_counter **counters, unsigned num)
- {
- int i;
- for (i = 0; i < (int)num; i++)
- {
- if (counters[i]->call_cnt)
- {
- RARCH_LOG(PERF_LOG_FMT,
- counters[i]->ident,
- (uint64_t)counters[i]->total /
- (uint64_t)counters[i]->call_cnt,
- (uint64_t)counters[i]->call_cnt);
- }
- }
- }
- static void runloop_perf_log(void)
- {
- RARCH_LOG("[PERF]: Performance counters (libretro):\n");
- runloop_log_counters(runloop_state.perf_counters_libretro,
- runloop_state.perf_ptr_libretro);
- }
- static bool runloop_environ_cb_get_system_info(unsigned cmd, void *data)
- {
- runloop_state_t *runloop_st = &runloop_state;
- rarch_system_info_t *system = &runloop_st->system;
- switch (cmd)
- {
- case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME:
- *runloop_st->load_no_content_hook = *(const bool*)data;
- break;
- case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO:
- {
- size_t i, j, size;
- const struct retro_subsystem_info *info =
- (const struct retro_subsystem_info*)data;
- settings_t *settings = config_get_ptr();
- unsigned log_level = settings->uints.libretro_log_level;
- runloop_st->subsystem_current_count = 0;
- RARCH_LOG("[Environ]: SET_SUBSYSTEM_INFO.\n");
- for (i = 0; info[i].ident; i++)
- {
- if (log_level != RETRO_LOG_DEBUG)
- continue;
- RARCH_DBG("Subsystem ID: %d\nSpecial game type: %s\n Ident: %s\n ID: %u\n Content:\n",
- i,
- info[i].desc,
- info[i].ident,
- info[i].id
- );
- for (j = 0; j < info[i].num_roms; j++)
- {
- RARCH_DBG(" %s (%s)\n",
- info[i].roms[j].desc, info[i].roms[j].required ?
- "required" : "optional");
- }
- }
- size = i;
- if (log_level == RETRO_LOG_DEBUG)
- {
- RARCH_DBG("Subsystems: %d\n", i);
- if (size > SUBSYSTEM_MAX_SUBSYSTEMS)
- RARCH_WARN("Subsystems exceed subsystem max, clamping to %d\n", SUBSYSTEM_MAX_SUBSYSTEMS);
- }
- if (system)
- {
- for (i = 0; i < size && i < SUBSYSTEM_MAX_SUBSYSTEMS; i++)
- {
- struct retro_subsystem_info *subsys_info = &runloop_st->subsystem_data[i];
- struct retro_subsystem_rom_info *subsys_rom_info = runloop_st->subsystem_data_roms[i];
- /* Nasty, but have to do it like this since
- * the pointers are const char *
- * (if we don't free them, we get a memory leak) */
- if (!string_is_empty(subsys_info->desc))
- free((char *)subsys_info->desc);
- if (!string_is_empty(subsys_info->ident))
- free((char *)subsys_info->ident);
- subsys_info->desc = strdup(info[i].desc);
- subsys_info->ident = strdup(info[i].ident);
- subsys_info->id = info[i].id;
- subsys_info->num_roms = info[i].num_roms;
- if (log_level == RETRO_LOG_DEBUG)
- if (subsys_info->num_roms > SUBSYSTEM_MAX_SUBSYSTEM_ROMS)
- RARCH_WARN("Subsystems exceed subsystem max roms, clamping to %d\n", SUBSYSTEM_MAX_SUBSYSTEM_ROMS);
- for (j = 0; j < subsys_info->num_roms && j < SUBSYSTEM_MAX_SUBSYSTEM_ROMS; j++)
- {
- /* Nasty, but have to do it like this since
- * the pointers are const char *
- * (if we don't free them, we get a memory leak) */
- if (!string_is_empty(subsys_rom_info[j].desc))
- free((char *)
- subsys_rom_info[j].desc);
- if (!string_is_empty(
- subsys_rom_info[j].valid_extensions))
- free((char *)
- subsys_rom_info[j].valid_extensions);
- subsys_rom_info[j].desc =
- strdup(info[i].roms[j].desc);
- subsys_rom_info[j].valid_extensions =
- strdup(info[i].roms[j].valid_extensions);
- subsys_rom_info[j].required =
- info[i].roms[j].required;
- subsys_rom_info[j].block_extract =
- info[i].roms[j].block_extract;
- subsys_rom_info[j].need_fullpath =
- info[i].roms[j].need_fullpath;
- }
- subsys_info->roms = subsys_rom_info;
- }
- runloop_st->subsystem_current_count =
- size <= SUBSYSTEM_MAX_SUBSYSTEMS
- ? (unsigned)size
- : SUBSYSTEM_MAX_SUBSYSTEMS;
- }
- break;
- }
- default:
- return false;
- }
- return true;
- }
- #ifdef HAVE_DYNAMIC
- /**
- * libretro_get_environment_info:
- * @func : Function pointer for get_environment_info.
- * @load_no_content : If true, core should be able to auto-start
- * without any content loaded.
- *
- * Sets environment callback in order to get statically known
- * information from it.
- *
- * Fetched via environment callbacks instead of
- * retro_get_system_info(), as this info is part of extensions.
- *
- * Should only be called once right after core load to
- * avoid overwriting the "real" environ callback.
- *
- * For statically linked cores, pass retro_set_environment as argument.
- */
- void libretro_get_environment_info(
- void (*func)(retro_environment_t),
- bool *load_no_content)
- {
- runloop_state_t *runloop_st = &runloop_state;
- runloop_st->load_no_content_hook = load_no_content;
- /* load_no_content gets set in this callback. */
- func(runloop_environ_cb_get_system_info);
- /* It's possible that we just set get_system_info callback
- * to the currently running core.
- *
- * Make sure we reset it to the actual environment callback.
- * Ignore any environment callbacks here in case we're running
- * on the non-current core. */
- runloop_st->flags |= RUNLOOP_FLAG_IGNORE_ENVIRONMENT_CB;
- func(runloop_environment_cb);
- runloop_st->flags &= ~RUNLOOP_FLAG_IGNORE_ENVIRONMENT_CB;
- }
- static dylib_t load_dynamic_core(const char *path, char *buf,
- size_t size)
- {
- #if defined(ANDROID)
- /* Can't resolve symlinks when dealing with cores
- * installed via play feature delivery, because the
- * source files have non-standard file names (which
- * will not be recognised by regular core handling
- * routines) */
- bool resolve_symlinks = !play_feature_delivery_enabled();
- #else
- bool resolve_symlinks = true;
- #endif
- /* Can't lookup symbols in itself on UWP */
- #if !(defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
- if (dylib_proc(NULL, "retro_init"))
- {
- /* Try to verify that -lretro was not linked in from other modules
- * since loading it dynamically and with -l will fail hard. */
- RARCH_ERR("Serious problem. RetroArch wants to load libretro cores"
- " dynamically, but it is already linked.\n");
- RARCH_ERR("This could happen if other modules RetroArch depends on "
- "link against libretro directly.\n");
- RARCH_ERR("Proceeding could cause a crash. Aborting ...\n");
- retroarch_fail(1, "load_dynamic_core()");
- }
- #endif
- /* Need to use absolute path for this setting. It can be
- * saved to content history, and a relative path would
- * break in that scenario. */
- path_resolve_realpath(buf, size, resolve_symlinks);
- return dylib_load(path);
- }
- static dylib_t libretro_get_system_info_lib(const char *path,
- struct retro_system_info *info, bool *load_no_content)
- {
- dylib_t lib = dylib_load(path);
- void (*proc)(struct retro_system_info*);
- if (!lib)
- return NULL;
- proc = (void (*)(struct retro_system_info*))
- dylib_proc(lib, "retro_get_system_info");
- if (!proc)
- {
- dylib_close(lib);
- return NULL;
- }
- proc(info);
- if (load_no_content)
- {
- void (*set_environ)(retro_environment_t);
- *load_no_content = false;
- set_environ = (void (*)(retro_environment_t))
- dylib_proc(lib, "retro_set_environment");
- if (set_environ)
- libretro_get_environment_info(set_environ, load_no_content);
- }
- return lib;
- }
- #endif
- static void runloop_update_runtime_log(
- runloop_state_t *runloop_st,
- const char *dir_runtime_log,
- const char *dir_playlist,
- bool log_per_core)
- {
- /* Initialise runtime log file */
- runtime_log_t *runtime_log = runtime_log_init(
- runloop_st->runtime_content_path,
- runloop_st->runtime_core_path,
- dir_runtime_log,
- dir_playlist,
- log_per_core);
- if (!runtime_log)
- return;
- /* Add additional runtime */
- runtime_log_add_runtime_usec(runtime_log,
- runloop_st->core_runtime_usec);
- /* Update 'last played' entry */
- runtime_log_set_last_played_now(runtime_log);
- /* Save runtime log file */
- runtime_log_save(runtime_log);
- /* Clean up */
- free(runtime_log);
- }
- void runloop_runtime_log_deinit(
- runloop_state_t *runloop_st,
- bool content_runtime_log,
- bool content_runtime_log_aggregate,
- const char *dir_runtime_log,
- const char *dir_playlist)
- {
- if (verbosity_is_enabled())
- {
- char log[PATH_MAX_LENGTH] = {0};
- unsigned hours = 0;
- unsigned minutes = 0;
- unsigned seconds = 0;
- runtime_log_convert_usec2hms(
- runloop_st->core_runtime_usec,
- &hours, &minutes, &seconds);
- /* TODO/FIXME - localize */
- snprintf(log, sizeof(log),
- "[Core]: Content ran for a total of:"
- " %02u hours, %02u minutes, %02u seconds.",
- hours, minutes, seconds);
- RARCH_LOG("%s\n", log);
- }
- /* Only write to file if content has run for a non-zero length of time */
- if (runloop_st->core_runtime_usec > 0)
- {
- /* Per core logging */
- if (content_runtime_log)
- runloop_update_runtime_log(runloop_st, dir_runtime_log, dir_playlist, true);
- /* Aggregate logging */
- if (content_runtime_log_aggregate)
- runloop_update_runtime_log(runloop_st, dir_runtime_log, dir_playlist, false);
- }
- /* Reset runtime + content/core paths, to prevent any
- * possibility of duplicate logging */
- runloop_st->core_runtime_usec = 0;
- memset(runloop_st->runtime_content_path, 0,
- sizeof(runloop_st->runtime_content_path));
- memset(runloop_st->runtime_core_path, 0,
- sizeof(runloop_st->runtime_core_path));
- }
- static bool runloop_clear_all_thread_waits(
- unsigned clear_threads, void *data)
- {
- if (clear_threads > 0)
- audio_driver_start(false);
- else
- audio_driver_stop();
- return true;
- }
- static bool dynamic_verify_hw_context(
- const char *video_ident,
- bool driver_switch_enable,
- enum retro_hw_context_type type,
- unsigned minor, unsigned major)
- {
- if (!driver_switch_enable)
- {
- switch (type)
- {
- case RETRO_HW_CONTEXT_VULKAN:
- if (!string_is_equal(video_ident, "vulkan"))
- return false;
- break;
- #if defined(HAVE_OPENGL_CORE)
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- if (!string_is_equal(video_ident, "glcore"))
- return false;
- break;
- #else
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- #endif
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGLES3:
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- case RETRO_HW_CONTEXT_OPENGL:
- if (!string_is_equal(video_ident, "gl") &&
- !string_is_equal(video_ident, "glcore"))
- return false;
- break;
- case RETRO_HW_CONTEXT_D3D10:
- if (!string_is_equal(video_ident, "d3d10"))
- return false;
- break;
- case RETRO_HW_CONTEXT_D3D11:
- if (!string_is_equal(video_ident, "d3d11"))
- return false;
- break;
- case RETRO_HW_CONTEXT_D3D12:
- if (!string_is_equal(video_ident, "d3d12"))
- return false;
- break;
- default:
- break;
- }
- }
- return true;
- }
- static bool dynamic_request_hw_context(enum retro_hw_context_type type,
- unsigned minor, unsigned major)
- {
- switch (type)
- {
- case RETRO_HW_CONTEXT_NONE:
- RARCH_LOG("Requesting no HW context.\n");
- break;
- case RETRO_HW_CONTEXT_VULKAN:
- #ifdef HAVE_VULKAN
- RARCH_LOG("Requesting Vulkan context.\n");
- break;
- #else
- RARCH_ERR("Requesting Vulkan context, but RetroArch is not compiled against Vulkan. Cannot use HW context.\n");
- return false;
- #endif
- #if defined(HAVE_OPENGLES)
- #if (defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3))
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGLES3:
- RARCH_LOG("Requesting OpenGLES%u context.\n",
- type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3);
- break;
- #if defined(HAVE_OPENGLES3)
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- #ifndef HAVE_OPENGLES3_2
- if (major == 3 && minor == 2)
- {
- RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch is compiled against a lesser version. Cannot use HW context.\n",
- major, minor);
- return false;
- }
- #endif
- #if !defined(HAVE_OPENGLES3_2) && !defined(HAVE_OPENGLES3_1)
- if (major == 3 && minor == 1)
- {
- RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch is compiled against a lesser version. Cannot use HW context.\n",
- major, minor);
- return false;
- }
- #endif
- RARCH_LOG("Requesting OpenGLES%u.%u context.\n",
- major, minor);
- break;
- #endif
- #endif
- case RETRO_HW_CONTEXT_OPENGL:
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- RARCH_ERR("Requesting OpenGL context, but RetroArch "
- "is compiled against OpenGLES. Cannot use HW context.\n");
- return false;
- #elif defined(HAVE_OPENGL) || defined(HAVE_OPENGL_CORE)
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGLES3:
- RARCH_ERR("Requesting OpenGLES%u context, but RetroArch "
- "is compiled against OpenGL. Cannot use HW context.\n",
- type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3);
- return false;
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch "
- "is compiled against OpenGL. Cannot use HW context.\n",
- major, minor);
- return false;
- case RETRO_HW_CONTEXT_OPENGL:
- RARCH_LOG("Requesting OpenGL context.\n");
- break;
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- /* TODO/FIXME - we should do a check here to see if
- * the requested core GL version is supported */
- RARCH_LOG("Requesting core OpenGL context (%u.%u).\n",
- major, minor);
- break;
- #endif
- #if defined(HAVE_D3D11)
- case RETRO_HW_CONTEXT_D3D11:
- RARCH_LOG("Requesting D3D11 context.\n");
- break;
- #endif
- #ifdef HAVE_D3D10
- case RETRO_HW_CONTEXT_D3D10:
- RARCH_LOG("Requesting D3D10 context.\n");
- break;
- #endif
- #ifdef HAVE_D3D12
- case RETRO_HW_CONTEXT_D3D12:
- RARCH_LOG("Requesting D3D12 context.\n");
- break;
- #endif
- #if defined(HAVE_D3D9)
- case RETRO_HW_CONTEXT_D3D9:
- RARCH_LOG("Requesting D3D9 context.\n");
- break;
- #endif
- default:
- RARCH_LOG("Requesting unknown context.\n");
- return false;
- }
- return true;
- }
- static void libretro_log_cb(
- enum retro_log_level level,
- const char *fmt, ...)
- {
- va_list vp;
- settings_t *settings = config_get_ptr();
- unsigned libretro_log_level = settings->uints.libretro_log_level;
- if ((unsigned)level < libretro_log_level)
- return;
- if (!verbosity_is_enabled())
- return;
- va_start(vp, fmt);
- switch (level)
- {
- case RETRO_LOG_DEBUG:
- RARCH_LOG_V("[libretro DEBUG]", fmt, vp);
- break;
- case RETRO_LOG_INFO:
- RARCH_LOG_OUTPUT_V("[libretro INFO]", fmt, vp);
- break;
- case RETRO_LOG_WARN:
- RARCH_WARN_V("[libretro WARN]", fmt, vp);
- break;
- case RETRO_LOG_ERROR:
- RARCH_ERR_V("[libretro ERROR]", fmt, vp);
- break;
- default:
- break;
- }
- va_end(vp);
- }
- static size_t mmap_add_bits_down(size_t n)
- {
- n |= n >> 1;
- n |= n >> 2;
- n |= n >> 4;
- n |= n >> 8;
- n |= n >> 16;
- /* double shift to avoid warnings on 32bit (it's dead code,
- * but compilers suck) */
- if (sizeof(size_t) > 4)
- n |= n >> 16 >> 16;
- return n;
- }
- static size_t mmap_inflate(size_t addr, size_t mask)
- {
- while (mask)
- {
- size_t tmp = (mask - 1) & ~mask;
- /* to put in an 1 bit instead, OR in tmp+1 */
- addr = ((addr & ~tmp) << 1) | (addr & tmp);
- mask = mask & (mask - 1);
- }
- return addr;
- }
- static size_t mmap_reduce(size_t addr, size_t mask)
- {
- while (mask)
- {
- size_t tmp = (mask - 1) & ~mask;
- addr = (addr & tmp) | ((addr >> 1) & ~tmp);
- mask = (mask & (mask - 1)) >> 1;
- }
- return addr;
- }
- static size_t mmap_highest_bit(size_t n)
- {
- n = mmap_add_bits_down(n);
- return n ^ (n >> 1);
- }
- static bool mmap_preprocess_descriptors(
- rarch_memory_descriptor_t *first, unsigned count)
- {
- size_t top_addr = 1;
- rarch_memory_descriptor_t *desc = NULL;
- const rarch_memory_descriptor_t *end = first + count;
- size_t highest_reachable = 0;
- for (desc = first; desc < end; desc++)
- {
- if (desc->core.select != 0)
- top_addr |= desc->core.select;
- else
- top_addr |= desc->core.start + desc->core.len - 1;
- }
- top_addr = mmap_add_bits_down(top_addr);
- for (desc = first; desc < end; desc++)
- {
- if (desc->core.select == 0)
- {
- if (desc->core.len == 0)
- return false;
- if ((desc->core.len & (desc->core.len - 1)) != 0)
- return false;
- desc->core.select = top_addr
- & ~mmap_inflate(mmap_add_bits_down(desc->core.len - 1),
- desc->core.disconnect);
- }
- if (desc->core.len == 0)
- desc->core.len = mmap_add_bits_down(
- mmap_reduce(top_addr & ~desc->core.select,
- desc->core.disconnect)) + 1;
- if (desc->core.start & ~desc->core.select)
- return false;
- highest_reachable = mmap_inflate(desc->core.len - 1,
- desc->core.disconnect);
- /* Disconnect unselected bits that are too high to ever
- * index into the core's buffer. Higher addresses will
- * repeat / mirror the buffer as long as they match select */
- while (mmap_highest_bit(top_addr
- & ~desc->core.select
- & ~desc->core.disconnect) >
- mmap_highest_bit(highest_reachable))
- desc->core.disconnect |= mmap_highest_bit(top_addr
- & ~desc->core.select
- & ~desc->core.disconnect);
- }
- return true;
- }
- static void runloop_deinit_core_options(
- bool game_options_active,
- const char *path_core_options,
- core_option_manager_t *core_options)
- {
- /* Check whether game-specific options file is being used */
- if (!string_is_empty(path_core_options))
- {
- config_file_t *conf_tmp = NULL;
- /* We only need to save configuration settings for
- * the current core
- * > If game-specific options file exists, have
- * to read it (to ensure file only gets written
- * if config values change)
- * > Otherwise, create a new, empty config_file_t
- * object */
- if (path_is_valid(path_core_options))
- conf_tmp = config_file_new_from_path_to_string(path_core_options);
- if (!conf_tmp)
- conf_tmp = config_file_new_alloc();
- if (conf_tmp)
- {
- core_option_manager_flush(
- core_options,
- conf_tmp);
- RARCH_LOG("[Core]: Saved %s-specific core options to \"%s\".\n",
- game_options_active ? "game" : "folder", path_core_options);
- config_file_write(conf_tmp, path_core_options, true);
- config_file_free(conf_tmp);
- conf_tmp = NULL;
- }
- path_clear(RARCH_PATH_CORE_OPTIONS);
- }
- else
- {
- const char *path = core_options->conf_path;
- core_option_manager_flush(
- core_options,
- core_options->conf);
- RARCH_LOG("[Core]: Saved core options file to \"%s\".\n", path);
- config_file_write(core_options->conf, path, true);
- }
- if (core_options)
- core_option_manager_free(core_options);
- }
- static bool validate_per_core_options(char *s,
- size_t len, bool mkdir,
- const char *core_name, const char *game_name)
- {
- char config_directory[PATH_MAX_LENGTH];
- config_directory[0] = '\0';
- if ( (!s)
- || (len < 1)
- || string_is_empty(core_name)
- || string_is_empty(game_name))
- return false;
- fill_pathname_application_special(config_directory,
- sizeof(config_directory), APPLICATION_SPECIAL_DIRECTORY_CONFIG);
- fill_pathname_join_special_ext(s,
- config_directory, core_name, game_name,
- ".opt", len);
- /* No need to make a directory if file already exists... */
- if (mkdir && !path_is_valid(s))
- {
- char new_path[PATH_MAX_LENGTH];
- fill_pathname_join_special(new_path,
- config_directory, core_name, sizeof(new_path));
- if (!path_is_directory(new_path))
- path_mkdir(new_path);
- }
- return true;
- }
- static bool validate_game_options(
- const char *core_name,
- char *s, size_t len, bool mkdir)
- {
- const char *game_name = path_basename_nocompression(path_get(RARCH_PATH_BASENAME));
- return validate_per_core_options(s, len, mkdir,
- core_name, game_name);
- }
- /**
- * game_specific_options:
- *
- * @return true if a game specific core
- * options path has been found, otherwise false.
- **/
- static bool validate_game_specific_options(char **output)
- {
- char game_options_path[PATH_MAX_LENGTH];
- runloop_state_t *runloop_st = &runloop_state;
- game_options_path[0] = '\0';
- if (!validate_game_options(
- runloop_st->system.info.library_name,
- game_options_path,
- sizeof(game_options_path), false) ||
- !path_is_valid(game_options_path))
- return false;
- RARCH_LOG("[Core]: %s \"%s\".\n",
- msg_hash_to_str(MSG_GAME_SPECIFIC_CORE_OPTIONS_FOUND_AT),
- game_options_path);
- *output = strdup(game_options_path);
- return true;
- }
- static bool validate_folder_options(
- char *s, size_t len, bool mkdir)
- {
- char folder_name[PATH_MAX_LENGTH];
- runloop_state_t *runloop_st = &runloop_state;
- const char *core_name = runloop_st->system.info.library_name;
- const char *game_path = path_get(RARCH_PATH_BASENAME);
- folder_name[0] = '\0';
- if (string_is_empty(game_path))
- return false;
- fill_pathname_parent_dir_name(folder_name,
- game_path, sizeof(folder_name));
- return validate_per_core_options(s, len, mkdir,
- core_name, folder_name);
- }
- /**
- * validate_folder_specific_options:
- *
- * @return true if a folder specific core
- * options path has been found, otherwise false.
- **/
- static bool validate_folder_specific_options(
- char **output)
- {
- char folder_options_path[PATH_MAX_LENGTH];
- folder_options_path[0] ='\0';
- if (!validate_folder_options(
- folder_options_path,
- sizeof(folder_options_path), false) ||
- !path_is_valid(folder_options_path))
- return false;
- RARCH_LOG("[Core]: %s \"%s\".\n",
- msg_hash_to_str(MSG_FOLDER_SPECIFIC_CORE_OPTIONS_FOUND_AT),
- folder_options_path);
- *output = strdup(folder_options_path);
- return true;
- }
- /**
- * runloop_init_core_options_path:
- *
- * Fetches core options path for current core/content
- * - path: path from which options should be read
- * from/saved to
- * - src_path: in the event that 'path' file does not
- * yet exist, provides source path from which initial
- * options should be extracted
- *
- * NOTE: caller must ensure
- * path and src_path are NULL-terminated
- *
- **/
- static void runloop_init_core_options_path(
- settings_t *settings,
- char *path, size_t len,
- char *src_path, size_t src_len)
- {
- char *game_options_path = NULL;
- char *folder_options_path = NULL;
- runloop_state_t *runloop_st = &runloop_state;
- bool game_specific_options = settings->bools.game_specific_options;
- /* Check whether game-specific options exist */
- if (game_specific_options &&
- validate_game_specific_options(&game_options_path))
- {
- /* Notify system that we have a valid core options
- * override */
- path_set(RARCH_PATH_CORE_OPTIONS, game_options_path);
- runloop_st->flags &= ~RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE;
- runloop_st->flags |= RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE;
- /* Copy options path */
- strlcpy(path, game_options_path, len);
- free(game_options_path);
- }
- /* Check whether folder-specific options exist */
- else if (game_specific_options &&
- validate_folder_specific_options(
- &folder_options_path))
- {
- /* Notify system that we have a valid core options
- * override */
- path_set(RARCH_PATH_CORE_OPTIONS, folder_options_path);
- runloop_st->flags &= ~RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE;
- runloop_st->flags |= RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE;
- /* Copy options path */
- strlcpy(path, folder_options_path, len);
- free(folder_options_path);
- }
- else
- {
- char global_options_path[PATH_MAX_LENGTH];
- char per_core_options_path[PATH_MAX_LENGTH];
- bool per_core_options_exist = false;
- bool per_core_options = !settings->bools.global_core_options;
- const char *path_core_options = settings->paths.path_core_options;
- per_core_options_path[0] = '\0';
- if (per_core_options)
- {
- const char *core_name = runloop_st->system.info.library_name;
- /* Get core-specific options path
- * > if validate_per_core_options() returns
- * false, then per-core options are disabled (due to
- * unknown system errors...) */
- per_core_options = validate_per_core_options(
- per_core_options_path, sizeof(per_core_options_path), true,
- core_name, core_name);
- /* If we can use per-core options, check whether an options
- * file already exists */
- if (per_core_options)
- per_core_options_exist = path_is_valid(per_core_options_path);
- }
- /* If not using per-core options, or if a per-core options
- * file does not yet exist, must fetch 'global' options path */
- if (!per_core_options || !per_core_options_exist)
- {
- const char *options_path = path_core_options;
- if (!string_is_empty(options_path))
- strlcpy(global_options_path,
- options_path, sizeof(global_options_path));
- else if (!path_is_empty(RARCH_PATH_CONFIG))
- fill_pathname_resolve_relative(
- global_options_path, path_get(RARCH_PATH_CONFIG),
- FILE_PATH_CORE_OPTIONS_CONFIG, sizeof(global_options_path));
- }
- /* Allocate correct path/src_path strings */
- if (per_core_options)
- {
- strlcpy(path, per_core_options_path, len);
- if (!per_core_options_exist)
- strlcpy(src_path, global_options_path, src_len);
- }
- else
- strlcpy(path, global_options_path, len);
- /* Notify system that we *do not* have a valid core options
- * options override */
- runloop_st->flags &= ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- }
- }
- static core_option_manager_t *runloop_init_core_options(
- settings_t *settings,
- const struct retro_core_options_v2 *options_v2)
- {
- bool categories_enabled = settings->bools.core_option_category_enable;
- char options_path[PATH_MAX_LENGTH];
- char src_options_path[PATH_MAX_LENGTH];
- /* Ensure these are NULL-terminated */
- options_path[0] = '\0';
- src_options_path[0] = '\0';
- /* Get core options file path */
- runloop_init_core_options_path(settings,
- options_path, sizeof(options_path),
- src_options_path, sizeof(src_options_path));
- if (!string_is_empty(options_path))
- return core_option_manager_new(options_path,
- src_options_path, options_v2,
- categories_enabled);
- return NULL;
- }
- static core_option_manager_t *runloop_init_core_variables(
- settings_t *settings, const struct retro_variable *vars)
- {
- char options_path[PATH_MAX_LENGTH];
- char src_options_path[PATH_MAX_LENGTH];
- /* Ensure these are NULL-terminated */
- options_path[0] = '\0';
- src_options_path[0] = '\0';
- /* Get core options file path */
- runloop_init_core_options_path(
- settings,
- options_path, sizeof(options_path),
- src_options_path, sizeof(src_options_path));
- if (!string_is_empty(options_path))
- return core_option_manager_new_vars(options_path, src_options_path, vars);
- return NULL;
- }
- static void runloop_core_msg_queue_push(
- struct retro_system_av_info *av_info,
- const struct retro_message_ext *msg)
- {
- double fps;
- unsigned duration_frames;
- enum message_queue_category category;
- /* Assign category */
- switch (msg->level)
- {
- case RETRO_LOG_WARN:
- category = MESSAGE_QUEUE_CATEGORY_WARNING;
- break;
- case RETRO_LOG_ERROR:
- category = MESSAGE_QUEUE_CATEGORY_ERROR;
- break;
- case RETRO_LOG_INFO:
- case RETRO_LOG_DEBUG:
- default:
- category = MESSAGE_QUEUE_CATEGORY_INFO;
- break;
- }
- /* Get duration in frames */
- fps = (av_info && (av_info->timing.fps > 0)) ? av_info->timing.fps : 60.0;
- duration_frames = (unsigned)((fps * (float)msg->duration / 1000.0f) + 0.5f);
- /* Note: Do not flush the message queue here - a core
- * may need to send multiple notifications simultaneously */
- runloop_msg_queue_push(msg->msg,
- msg->priority, duration_frames,
- false, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
- category);
- }
- static void core_performance_counter_start(
- struct retro_perf_counter *perf)
- {
- runloop_state_t *runloop_st = &runloop_state;
- bool runloop_perfcnt_enable = runloop_st->perfcnt_enable;
- if (runloop_perfcnt_enable)
- {
- perf->call_cnt++;
- perf->start = cpu_features_get_perf_counter();
- }
- }
- static void core_performance_counter_stop(struct retro_perf_counter *perf)
- {
- runloop_state_t *runloop_st = &runloop_state;
- bool runloop_perfcnt_enable = runloop_st->perfcnt_enable;
- if (runloop_perfcnt_enable)
- perf->total += cpu_features_get_perf_counter() - perf->start;
- }
- bool runloop_environment_cb(unsigned cmd, void *data)
- {
- unsigned p;
- runloop_state_t *runloop_st = &runloop_state;
- recording_state_t *recording_st = recording_state_get_ptr();
- settings_t *settings = config_get_ptr();
- rarch_system_info_t *system = &runloop_st->system;
- bool ignore_environment_cb = runloop_st->flags &
- RUNLOOP_FLAG_IGNORE_ENVIRONMENT_CB;
- if (ignore_environment_cb)
- return false;
- /* RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE gets called
- * by every core on every frame. Handle it first,
- * to avoid the overhead of traversing the subsequent
- * (enormous) case statement */
- if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE)
- {
- if (runloop_st->core_options)
- *(bool*)data = runloop_st->core_options->updated;
- else
- *(bool*)data = false;
- return true;
- }
- switch (cmd)
- {
- case RETRO_ENVIRONMENT_GET_OVERSCAN:
- {
- bool video_crop_overscan = settings->bools.video_crop_overscan;
- *(bool*)data = !video_crop_overscan;
- RARCH_LOG("[Environ]: GET_OVERSCAN: %u\n",
- (unsigned)!video_crop_overscan);
- }
- break;
- case RETRO_ENVIRONMENT_GET_CAN_DUPE:
- *(bool*)data = true;
- RARCH_LOG("[Environ]: GET_CAN_DUPE: true\n");
- break;
- case RETRO_ENVIRONMENT_GET_VARIABLE:
- {
- struct retro_variable *var = (struct retro_variable*)data;
- size_t opt_idx;
- if (!var)
- return true;
- var->value = NULL;
- if (!runloop_st->core_options)
- {
- RARCH_ERR("[Environ]: GET_VARIABLE: %s - %s.\n",
- var->key, "Not implemented");
- return true;
- }
- #ifdef HAVE_RUNAHEAD
- if (runloop_st->core_options->updated)
- runloop_st->flags |= RUNLOOP_FLAG_HAS_VARIABLE_UPDATE;
- #endif
- runloop_st->core_options->updated = false;
- if (core_option_manager_get_idx(runloop_st->core_options,
- var->key, &opt_idx))
- var->value = core_option_manager_get_val(
- runloop_st->core_options, opt_idx);
- if (!var->value)
- {
- RARCH_ERR("[Environ]: GET_VARIABLE: %s - %s.\n",
- var->key, "Invalid value");
- return true;
- }
- RARCH_DBG("[Environ]: GET_VARIABLE: %s = \"%s\"\n",
- var->key, var->value);
- }
- break;
- case RETRO_ENVIRONMENT_SET_VARIABLE:
- {
- const struct retro_variable *var = (const struct retro_variable*)data;
- size_t opt_idx;
- size_t val_idx;
- /* If core passes NULL to the callback, return
- * value indicates whether callback is supported */
- if (!var)
- return true;
- if ( string_is_empty(var->key)
- || string_is_empty(var->value))
- return false;
- if (!runloop_st->core_options)
- {
- RARCH_ERR("[Environ]: SET_VARIABLE: %s - %s.\n",
- var->key, "Not implemented");
- return false;
- }
- /* Check whether key is valid */
- if (!core_option_manager_get_idx(runloop_st->core_options,
- var->key, &opt_idx))
- {
- RARCH_ERR("[Environ]: SET_VARIABLE: %s - %s.\n",
- var->key, "Invalid key");
- return false;
- }
- /* Check whether value is valid */
- if (!core_option_manager_get_val_idx(runloop_st->core_options,
- opt_idx, var->value, &val_idx))
- {
- RARCH_ERR("[Environ]: SET_VARIABLE: %s - %s: %s\n",
- var->key, "Invalid value", var->value);
- return false;
- }
- /* Update option value if core-requested value
- * is not currently set */
- if (val_idx != runloop_st->core_options->opts[opt_idx].index)
- core_option_manager_set_val(runloop_st->core_options,
- opt_idx, val_idx, true);
- RARCH_DBG("[Environ]: SET_VARIABLE: %s = \"%s\"\n",
- var->key, var->value);
- }
- break;
- /* SET_VARIABLES: Legacy path */
- case RETRO_ENVIRONMENT_SET_VARIABLES:
- RARCH_LOG("[Environ]: SET_VARIABLES.\n");
- {
- core_option_manager_t *new_vars = NULL;
- if (runloop_st->core_options)
- {
- runloop_deinit_core_options(
- runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE,
- path_get(RARCH_PATH_CORE_OPTIONS),
- runloop_st->core_options);
- runloop_st->flags &=
- ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- runloop_st->core_options = NULL;
- }
- if ((new_vars = runloop_init_core_variables(
- settings,
- (const struct retro_variable *)data)))
- runloop_st->core_options = new_vars;
- }
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS:
- RARCH_LOG("[Environ]: SET_CORE_OPTIONS.\n");
- {
- /* Parse core_option_definition array to
- * create retro_core_options_v2 struct */
- struct retro_core_options_v2 *options_v2 =
- core_option_manager_convert_v1(
- (const struct retro_core_option_definition*)data);
- if (runloop_st->core_options)
- {
- runloop_deinit_core_options(
- runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE,
- path_get(RARCH_PATH_CORE_OPTIONS),
- runloop_st->core_options);
- runloop_st->flags &=
- ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- runloop_st->core_options = NULL;
- }
- if (options_v2)
- {
- /* Initialise core options */
- core_option_manager_t *new_vars = runloop_init_core_options(settings, options_v2);
- if (new_vars)
- runloop_st->core_options = new_vars;
- /* Clean up */
- core_option_manager_free_converted(options_v2);
- }
- }
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL:
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL.\n");
- {
- /* Parse core_options_intl to create
- * retro_core_options_v2 struct */
- struct retro_core_options_v2 *options_v2 =
- core_option_manager_convert_v1_intl(
- (const struct retro_core_options_intl*)data);
- if (runloop_st->core_options)
- {
- runloop_deinit_core_options(
- runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE,
- path_get(RARCH_PATH_CORE_OPTIONS),
- runloop_st->core_options);
- runloop_st->flags &=
- ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- runloop_st->core_options = NULL;
- }
- if (options_v2)
- {
- /* Initialise core options */
- core_option_manager_t *new_vars = runloop_init_core_options(settings, options_v2);
- if (new_vars)
- runloop_st->core_options = new_vars;
- /* Clean up */
- core_option_manager_free_converted(options_v2);
- }
- }
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2:
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2.\n");
- {
- core_option_manager_t *new_vars = NULL;
- const struct retro_core_options_v2 *options_v2 =
- (const struct retro_core_options_v2 *)data;
- bool categories_enabled =
- settings->bools.core_option_category_enable;
- if (runloop_st->core_options)
- {
- runloop_deinit_core_options(
- runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE,
- path_get(RARCH_PATH_CORE_OPTIONS),
- runloop_st->core_options);
- runloop_st->flags &=
- ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- runloop_st->core_options = NULL;
- }
- if (options_v2)
- {
- new_vars = runloop_init_core_options(settings, options_v2);
- if (new_vars)
- runloop_st->core_options = new_vars;
- }
- /* Return value does not indicate success.
- * Callback returns 'true' if core option
- * categories are supported/enabled,
- * otherwise 'false'. */
- return categories_enabled;
- }
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL:
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL.\n");
- {
- /* Parse retro_core_options_v2_intl to create
- * retro_core_options_v2 struct */
- core_option_manager_t *new_vars = NULL;
- struct retro_core_options_v2 *options_v2 =
- core_option_manager_convert_v2_intl(
- (const struct retro_core_options_v2_intl*)data);
- bool categories_enabled =
- settings->bools.core_option_category_enable;
- if (runloop_st->core_options)
- {
- runloop_deinit_core_options(
- runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE,
- path_get(RARCH_PATH_CORE_OPTIONS),
- runloop_st->core_options);
- runloop_st->flags &=
- ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- runloop_st->core_options = NULL;
- }
- if (options_v2)
- {
- /* Initialise core options */
- new_vars = runloop_init_core_options(settings, options_v2);
- if (new_vars)
- runloop_st->core_options = new_vars;
- /* Clean up */
- core_option_manager_free_converted(options_v2);
- }
- /* Return value does not indicate success.
- * Callback returns 'true' if core option
- * categories are supported/enabled,
- * otherwise 'false'. */
- return categories_enabled;
- }
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY:
- RARCH_DBG("[Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY.\n");
- {
- const struct retro_core_option_display *core_options_display =
- (const struct retro_core_option_display *)data;
- if (runloop_st->core_options && core_options_display)
- core_option_manager_set_visible(
- runloop_st->core_options,
- core_options_display->key,
- core_options_display->visible);
- }
- break;
- case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK:
- RARCH_DBG("[Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK.\n");
- {
- const struct retro_core_options_update_display_callback
- *update_display_callback =
- (const struct retro_core_options_update_display_callback*)data;
- if (update_display_callback &&
- update_display_callback->callback)
- runloop_st->core_options_callback.update_display =
- update_display_callback->callback;
- else
- runloop_st->core_options_callback.update_display = NULL;
- }
- break;
- case RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION:
- RARCH_LOG("[Environ]: GET_MESSAGE_INTERFACE_VERSION.\n");
- /* Current API version is 1 */
- *(unsigned *)data = 1;
- break;
- case RETRO_ENVIRONMENT_SET_MESSAGE:
- {
- const struct retro_message *msg = (const struct retro_message*)data;
- #if defined(HAVE_GFX_WIDGETS)
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- if (p_dispwidget->active)
- gfx_widget_set_libretro_message(
- msg->msg,
- roundf((float)msg->frames / 60.0f * 1000.0f));
- else
- #endif
- runloop_msg_queue_push(msg->msg, 3, msg->frames,
- true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
- MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("[Environ]: SET_MESSAGE: %s\n", msg->msg);
- break;
- }
- case RETRO_ENVIRONMENT_SET_MESSAGE_EXT:
- {
- const struct retro_message_ext *msg =
- (const struct retro_message_ext*)data;
- /* Log message, if required */
- if (msg->target != RETRO_MESSAGE_TARGET_OSD)
- {
- switch (msg->level)
- {
- case RETRO_LOG_DEBUG:
- RARCH_DBG("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- case RETRO_LOG_WARN:
- RARCH_WARN("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- case RETRO_LOG_ERROR:
- RARCH_ERR("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- case RETRO_LOG_INFO:
- default:
- RARCH_LOG("[Environ]: SET_MESSAGE_EXT: %s\n", msg->msg);
- break;
- }
- }
- /* Display message via OSD, if required */
- if (msg->target != RETRO_MESSAGE_TARGET_LOG)
- {
- switch (msg->type)
- {
- /* Handle 'status' messages */
- case RETRO_MESSAGE_TYPE_STATUS:
- /* Note: We need to lock a mutex here. Strictly
- * speaking, 'core_status_msg' is not part
- * of the message queue, but:
- * - It may be implemented as a queue in the future
- * - It seems unnecessary to create a new slock_t
- * object for this type of message when
- * _runloop_msg_queue_lock is already available
- * We therefore just call runloop_msg_queue_lock()/
- * runloop_msg_queue_unlock() in this case */
- RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
- /* If a message is already set, only overwrite
- * it if the new message has the same or higher
- * priority */
- if (!runloop_st->core_status_msg.set ||
- (runloop_st->core_status_msg.priority <= msg->priority))
- {
- if (!string_is_empty(msg->msg))
- {
- strlcpy(runloop_st->core_status_msg.str, msg->msg,
- sizeof(runloop_st->core_status_msg.str));
- runloop_st->core_status_msg.duration = (float)msg->duration;
- runloop_st->core_status_msg.set = true;
- }
- else
- {
- /* Ensure sane behaviour if core sends an
- * empty message */
- runloop_st->core_status_msg.str[0] = '\0';
- runloop_st->core_status_msg.priority = 0;
- runloop_st->core_status_msg.duration = 0.0f;
- runloop_st->core_status_msg.set = false;
- }
- }
- RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
- break;
- #if defined(HAVE_GFX_WIDGETS)
- /* Handle 'alternate' non-queued notifications */
- case RETRO_MESSAGE_TYPE_NOTIFICATION_ALT:
- {
- video_driver_state_t *video_st =
- video_state_get_ptr();
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- if (p_dispwidget->active)
- gfx_widget_set_libretro_message(
- msg->msg, msg->duration);
- else
- runloop_core_msg_queue_push(
- &video_st->av_info, msg);
- }
- break;
- /* Handle 'progress' messages */
- case RETRO_MESSAGE_TYPE_PROGRESS:
- {
- video_driver_state_t *video_st =
- video_state_get_ptr();
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- if (p_dispwidget->active)
- gfx_widget_set_progress_message(
- msg->msg, msg->duration,
- msg->priority, msg->progress);
- else
- runloop_core_msg_queue_push(
- &video_st->av_info, msg);
- }
- break;
- #endif
- /* Handle standard (queued) notifications */
- case RETRO_MESSAGE_TYPE_NOTIFICATION:
- default:
- {
- video_driver_state_t *video_st =
- video_state_get_ptr();
- runloop_core_msg_queue_push(
- &video_st->av_info, msg);
- }
- break;
- }
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_ROTATION:
- {
- unsigned rotation = *(const unsigned*)data;
- bool video_allow_rotate = settings->bools.video_allow_rotate;
- RARCH_LOG("[Environ]: SET_ROTATION: %u\n", rotation);
- if (system)
- system->core_requested_rotation = rotation;
- if (!video_allow_rotate)
- return false;
- if (system)
- system->rotation = rotation;
- if (!video_driver_set_rotation(rotation))
- return false;
- break;
- }
- case RETRO_ENVIRONMENT_SHUTDOWN:
- {
- #ifdef HAVE_MENU
- struct menu_state *menu_st = menu_state_get_ptr();
- #endif
- /* This case occurs when a core (internally)
- * requests a shutdown event */
- RARCH_LOG("[Environ]: SHUTDOWN.\n");
- runloop_st->flags |= RUNLOOP_FLAG_CORE_SHUTDOWN_INITIATED
- | RUNLOOP_FLAG_SHUTDOWN_INITIATED;
- #ifdef HAVE_MENU
- /* Ensure that menu stack is flushed appropriately
- * after the core has stopped running */
- if (menu_st)
- {
- const char *content_path = path_get(RARCH_PATH_CONTENT);
- menu_st->flags |= MENU_ST_FLAG_PENDING_ENV_SHUTDOWN_FLUSH;
- if (!string_is_empty(content_path))
- strlcpy(menu_st->pending_env_shutdown_content_path,
- content_path,
- sizeof(menu_st->pending_env_shutdown_content_path));
- else
- menu_st->pending_env_shutdown_content_path[0] = '\0';
- }
- #endif
- break;
- }
- case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL:
- if (system)
- {
- system->performance_level = *(const unsigned*)data;
- RARCH_LOG("[Environ]: PERFORMANCE_LEVEL: %u.\n",
- system->performance_level);
- }
- break;
- case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY:
- {
- const char *dir_system = settings->paths.directory_system;
- bool systemfiles_in_content_dir = settings->bools.systemfiles_in_content_dir;
- if (string_is_empty(dir_system) || systemfiles_in_content_dir)
- {
- const char *fullpath = path_get(RARCH_PATH_CONTENT);
- if (!string_is_empty(fullpath))
- {
- char tmp_path[PATH_MAX_LENGTH];
- if (string_is_empty(dir_system))
- RARCH_WARN("[Environ]: SYSTEM DIR is empty, assume CONTENT DIR %s\n",
- fullpath);
- strlcpy(tmp_path, fullpath, sizeof(tmp_path));
- path_basedir(tmp_path);
- dir_set(RARCH_DIR_SYSTEM, tmp_path);
- }
- *(const char**)data = dir_get_ptr(RARCH_DIR_SYSTEM);
- RARCH_LOG("[Environ]: SYSTEM_DIRECTORY: \"%s\".\n",
- dir_system);
- }
- else
- {
- *(const char**)data = dir_system;
- RARCH_LOG("[Environ]: SYSTEM_DIRECTORY: \"%s\".\n",
- dir_system);
- }
- }
- break;
- case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
- RARCH_LOG("[Environ]: GET_SAVE_DIRECTORY.\n");
- *(const char**)data = runloop_st->savefile_dir;
- break;
- case RETRO_ENVIRONMENT_GET_USERNAME:
- *(const char**)data = *settings->paths.username ?
- settings->paths.username : NULL;
- RARCH_LOG("[Environ]: GET_USERNAME: \"%s\".\n",
- settings->paths.username);
- break;
- case RETRO_ENVIRONMENT_GET_LANGUAGE:
- #ifdef HAVE_LANGEXTRA
- {
- unsigned user_lang = *msg_hash_get_uint(MSG_HASH_USER_LANGUAGE);
- *(unsigned *)data = user_lang;
- RARCH_LOG("[Environ]: GET_LANGUAGE: \"%u\".\n", user_lang);
- }
- #endif
- break;
- case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT:
- {
- video_driver_state_t *video_st =
- video_state_get_ptr();
- enum retro_pixel_format pix_fmt =
- *(const enum retro_pixel_format*)data;
- switch (pix_fmt)
- {
- case RETRO_PIXEL_FORMAT_0RGB1555:
- RARCH_LOG("[Environ]: SET_PIXEL_FORMAT: 0RGB1555.\n");
- break;
- case RETRO_PIXEL_FORMAT_RGB565:
- RARCH_LOG("[Environ]: SET_PIXEL_FORMAT: RGB565.\n");
- break;
- case RETRO_PIXEL_FORMAT_XRGB8888:
- RARCH_LOG("[Environ]: SET_PIXEL_FORMAT: XRGB8888.\n");
- break;
- default:
- return false;
- }
- video_st->pix_fmt = pix_fmt;
- break;
- }
- case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS:
- {
- static const char *libretro_btn_desc[] = {
- "B (bottom)", "Y (left)", "Select", "Start",
- "D-Pad Up", "D-Pad Down", "D-Pad Left", "D-Pad Right",
- "A (right)", "X (up)",
- "L", "R", "L2", "R2", "L3", "R3",
- };
- android_environment_cb_native(cmd, data);
- if (system)
- {
- unsigned retro_id;
- const struct retro_input_descriptor *desc = NULL;
- memset((void*)&system->input_desc_btn, 0,
- sizeof(system->input_desc_btn));
- desc = (const struct retro_input_descriptor*)data;
- for (; desc->description; desc++)
- {
- unsigned retro_port = desc->port;
- retro_id = desc->id;
- if (desc->port >= MAX_USERS)
- continue;
- if (desc->id >= RARCH_FIRST_CUSTOM_BIND)
- continue;
- switch (desc->device)
- {
- case RETRO_DEVICE_JOYPAD:
- system->input_desc_btn[retro_port]
- [retro_id] = desc->description;
- break;
- case RETRO_DEVICE_ANALOG:
- switch (retro_id)
- {
- case RETRO_DEVICE_ID_ANALOG_X:
- switch (desc->index)
- {
- case RETRO_DEVICE_INDEX_ANALOG_LEFT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_X_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_X_MINUS] = desc->description;
- break;
- case RETRO_DEVICE_INDEX_ANALOG_RIGHT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_X_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_X_MINUS] = desc->description;
- break;
- }
- break;
- case RETRO_DEVICE_ID_ANALOG_Y:
- switch (desc->index)
- {
- case RETRO_DEVICE_INDEX_ANALOG_LEFT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_Y_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_LEFT_Y_MINUS] = desc->description;
- break;
- case RETRO_DEVICE_INDEX_ANALOG_RIGHT:
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_Y_PLUS] = desc->description;
- system->input_desc_btn[retro_port]
- [RARCH_ANALOG_RIGHT_Y_MINUS] = desc->description;
- break;
- }
- break;
- case RETRO_DEVICE_ID_JOYPAD_R2:
- switch (desc->index)
- {
- case RETRO_DEVICE_INDEX_ANALOG_BUTTON:
- system->input_desc_btn[retro_port]
- [retro_id] = desc->description;
- break;
- }
- break;
- case RETRO_DEVICE_ID_JOYPAD_L2:
- switch (desc->index)
- {
- case RETRO_DEVICE_INDEX_ANALOG_BUTTON:
- system->input_desc_btn[retro_port]
- [retro_id] = desc->description;
- break;
- }
- break;
- }
- break;
- }
- }
- RARCH_LOG("[Environ]: SET_INPUT_DESCRIPTORS:\n");
- {
- unsigned log_level = settings->uints.libretro_log_level;
- if (log_level == RETRO_LOG_DEBUG)
- {
- unsigned input_driver_max_users = settings->uints.input_max_users;
- for (p = 0; p < input_driver_max_users; p++)
- {
- unsigned mapped_port = settings->uints.input_remap_ports[p];
- for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++)
- {
- const char *description = system->input_desc_btn[mapped_port][retro_id];
- if (!description)
- continue;
- RARCH_DBG(" RetroPad, Port %u, Button \"%s\" => \"%s\"\n",
- p + 1, libretro_btn_desc[retro_id], description);
- }
- }
- }
- }
- runloop_st->current_core.flags |=
- RETRO_CORE_FLAG_HAS_SET_INPUT_DESCRIPTORS;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK:
- {
- input_driver_state_t
- *input_st = input_state_get_ptr();
- const struct retro_keyboard_callback *info =
- (const struct retro_keyboard_callback*)data;
- retro_keyboard_event_t *frontend_key_event = &runloop_st->frontend_key_event;
- retro_keyboard_event_t *key_event = &runloop_st->key_event;
- RARCH_LOG("[Environ]: SET_KEYBOARD_CALLBACK.\n");
- if (key_event)
- *key_event = info->callback;
- if (frontend_key_event && key_event)
- *frontend_key_event = *key_event;
- /* If a core calls RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK,
- * then it is assumed that Game Focus mode is desired */
- input_st->game_focus_state.core_requested = true;
- break;
- }
- case RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION:
- RARCH_LOG("[Environ]: GET_DISK_CONTROL_INTERFACE_VERSION.\n");
- /* Current API version is 1 */
- *(unsigned *)data = 1;
- break;
- case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE:
- {
- const struct retro_disk_control_callback *control_cb =
- (const struct retro_disk_control_callback*)data;
- if (system)
- {
- RARCH_LOG("[Environ]: SET_DISK_CONTROL_INTERFACE.\n");
- disk_control_set_callback(&system->disk_control, control_cb);
- }
- }
- break;
- case RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE:
- {
- const struct retro_disk_control_ext_callback *control_cb =
- (const struct retro_disk_control_ext_callback*)data;
- if (system)
- {
- RARCH_LOG("[Environ]: SET_DISK_CONTROL_EXT_INTERFACE.\n");
- disk_control_set_ext_callback(&system->disk_control, control_cb);
- }
- }
- break;
- case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER:
- {
- unsigned *cb = (unsigned*)data;
- settings_t *settings = config_get_ptr();
- const char *video_driver_name = settings->arrays.video_driver;
- bool driver_switch_enable = settings->bools.driver_switch_enable;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER, video driver name: %s.\n", video_driver_name);
- if (string_is_equal(video_driver_name, "glcore"))
- {
- *cb = RETRO_HW_CONTEXT_OPENGL_CORE;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_OPENGL_CORE.\n");
- }
- else if (string_is_equal(video_driver_name, "gl"))
- {
- *cb = RETRO_HW_CONTEXT_OPENGL;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_OPENGL.\n");
- }
- else if (string_is_equal(video_driver_name, "vulkan"))
- {
- *cb = RETRO_HW_CONTEXT_VULKAN;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_VULKAN.\n");
- }
- else if (string_is_equal(video_driver_name, "d3d11"))
- {
- *cb = RETRO_HW_CONTEXT_D3D11;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_D3D11.\n");
- }
- else if (string_is_equal(video_driver_name, "d3d12"))
- {
- *cb = RETRO_HW_CONTEXT_D3D12;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_D3D12.\n");
- }
- else
- {
- *cb = RETRO_HW_CONTEXT_NONE;
- RARCH_LOG("[Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_NONE.\n");
- }
- if (!driver_switch_enable)
- {
- RARCH_LOG("[Environ]: Driver switching disabled, GET_PREFERRED_HW_RENDER will be ignored.\n");
- return false;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_HW_RENDER:
- case RETRO_ENVIRONMENT_SET_HW_RENDER | RETRO_ENVIRONMENT_EXPERIMENTAL:
- {
- settings_t *settings = config_get_ptr();
- struct retro_hw_render_callback *cb =
- (struct retro_hw_render_callback*)data;
- video_driver_state_t *video_st =
- video_state_get_ptr();
- struct retro_hw_render_callback *hwr =
- VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL(video_st);
- if (!cb)
- {
- RARCH_ERR("[Environ]: SET_HW_RENDER - No valid callback passed, returning...\n");
- return false;
- }
- RARCH_LOG("[Environ]: SET_HW_RENDER, context type: %s.\n", hw_render_context_name(cb->context_type, cb->version_major, cb->version_minor));
- if (!dynamic_request_hw_context(
- cb->context_type, cb->version_minor, cb->version_major))
- {
- RARCH_ERR("[Environ]: SET_HW_RENDER - Dynamic request HW context failed.\n");
- return false;
- }
- if (!dynamic_verify_hw_context(
- settings->arrays.video_driver,
- settings->bools.driver_switch_enable,
- cb->context_type, cb->version_minor, cb->version_major))
- {
- RARCH_ERR("[Environ]: SET_HW_RENDER: Dynamic verify HW context failed.\n");
- return false;
- }
- #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL_CORE)
- /* TODO/FIXME - should check first if an OpenGL
- * driver is running */
- if (cb->context_type == RETRO_HW_CONTEXT_OPENGL_CORE)
- {
- /* Ensure that the rest of the frontend knows
- * we have a core context */
- gfx_ctx_flags_t flags;
- flags.flags = 0;
- BIT32_SET(flags.flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT);
- video_context_driver_set_flags(&flags);
- }
- #endif
- cb->get_current_framebuffer = video_driver_get_current_framebuffer;
- cb->get_proc_address = video_driver_get_proc_address;
- /* Old ABI. Don't copy garbage. */
- if (cmd & RETRO_ENVIRONMENT_EXPERIMENTAL)
- {
- memcpy(hwr,
- cb, offsetof(struct retro_hw_render_callback, stencil));
- memset((uint8_t*)hwr + offsetof(struct retro_hw_render_callback, stencil),
- 0, sizeof(*cb) - offsetof(struct retro_hw_render_callback, stencil));
- }
- else
- memcpy(hwr, cb, sizeof(*cb));
- RARCH_DBG("Reached end of SET_HW_RENDER.\n");
- break;
- }
- case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME:
- {
- bool state = *(const bool*)data;
- RARCH_LOG("[Environ]: SET_SUPPORT_NO_GAME: %s.\n", state ? "yes" : "no");
- if (state)
- content_set_does_not_need_content();
- else
- content_unset_does_not_need_content();
- break;
- }
- case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH:
- {
- const char **path = (const char**)data;
- RARCH_LOG("[Environ]: GET_LIBRETRO_PATH.\n");
- #ifdef HAVE_DYNAMIC
- *path = path_get(RARCH_PATH_CORE);
- #else
- *path = NULL;
- #endif
- break;
- }
- case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK:
- #ifdef HAVE_THREADS
- {
- recording_state_t
- *recording_st = recording_state_get_ptr();
- audio_driver_state_t
- *audio_st = audio_state_get_ptr();
- const struct
- retro_audio_callback *cb = (const struct retro_audio_callback*)data;
- RARCH_LOG("[Environ]: SET_AUDIO_CALLBACK.\n");
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- return false;
- #endif
- if (recording_st->data) /* A/V sync is a must. */
- return false;
- if (cb)
- audio_st->callback = *cb;
- }
- break;
- #else
- return false;
- #endif
- case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK:
- {
- const struct retro_frame_time_callback *info =
- (const struct retro_frame_time_callback*)data;
- RARCH_LOG("[Environ]: SET_FRAME_TIME_CALLBACK.\n");
- #ifdef HAVE_NETWORKING
- /* retro_run() will be called in very strange and
- * mysterious ways, have to disable it. */
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- return false;
- #endif
- runloop_st->frame_time = *info;
- break;
- }
- case RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK:
- {
- const struct retro_audio_buffer_status_callback *info =
- (const struct retro_audio_buffer_status_callback*)data;
- RARCH_LOG("[Environ]: SET_AUDIO_BUFFER_STATUS_CALLBACK.\n");
- if (info)
- runloop_st->audio_buffer_status.callback = info->callback;
- else
- runloop_st->audio_buffer_status.callback = NULL;
- break;
- }
- case RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY:
- {
- unsigned audio_latency_default = settings->uints.audio_latency;
- unsigned audio_latency_current =
- (runloop_st->audio_latency > audio_latency_default) ?
- runloop_st->audio_latency : audio_latency_default;
- unsigned audio_latency_new;
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY.\n");
- /* Sanitise input latency value */
- runloop_st->audio_latency = 0;
- if (data)
- runloop_st->audio_latency = *(const unsigned*)data;
- if (runloop_st->audio_latency > 512)
- {
- RARCH_WARN("[Environ]: Requested audio latency of %u ms - limiting to maximum of 512 ms.\n",
- runloop_st->audio_latency);
- runloop_st->audio_latency = 512;
- }
- /* Determine new set-point latency value */
- if (runloop_st->audio_latency >= audio_latency_default)
- audio_latency_new = runloop_st->audio_latency;
- else
- {
- if (runloop_st->audio_latency != 0)
- RARCH_WARN("[Environ]: Requested audio latency of %u ms is less than frontend default of %u ms."
- " Using frontend default...\n",
- runloop_st->audio_latency, audio_latency_default);
- audio_latency_new = audio_latency_default;
- }
- /* Check whether audio driver requires reinitialisation
- * (Identical to RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO,
- * without video driver initialisation) */
- if (audio_latency_new != audio_latency_current)
- {
- recording_state_t
- *recording_st = recording_state_get_ptr();
- bool video_fullscreen = settings->bools.video_fullscreen;
- int reinit_flags = DRIVERS_CMD_ALL &
- ~(DRIVER_VIDEO_MASK | DRIVER_INPUT_MASK | DRIVER_MENU_MASK);
- RARCH_LOG("[Environ]: Setting audio latency to %u ms.\n", audio_latency_new);
- command_event(CMD_EVENT_REINIT, &reinit_flags);
- video_driver_set_aspect_ratio();
- /* Cannot continue recording with different
- * parameters.
- * Take the easiest route out and just restart
- * the recording. */
- if (recording_st->data)
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT),
- 2, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- }
- /* Hide mouse cursor in fullscreen mode */
- if (video_fullscreen)
- video_driver_hide_mouse();
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE:
- {
- struct retro_rumble_interface *iface =
- (struct retro_rumble_interface*)data;
- RARCH_LOG("[Environ]: GET_RUMBLE_INTERFACE.\n");
- iface->set_rumble_state = input_set_rumble_state;
- break;
- }
- case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES:
- {
- uint64_t *mask = (uint64_t*)data;
- input_driver_state_t
- *input_st = input_state_get_ptr();
- RARCH_LOG("[Environ]: GET_INPUT_DEVICE_CAPABILITIES.\n");
- if ( !input_st->current_driver->get_capabilities
- || !input_st->current_data)
- return false;
- *mask = input_driver_get_capabilities();
- break;
- }
- case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE:
- {
- settings_t *settings = config_get_ptr();
- bool input_sensors_enable = settings->bools.input_sensors_enable;
- struct retro_sensor_interface *iface = (struct retro_sensor_interface*)data;
- RARCH_LOG("[Environ]: GET_SENSOR_INTERFACE.\n");
- if (!input_sensors_enable)
- return false;
- iface->set_sensor_state = input_set_sensor_state;
- iface->get_sensor_input = input_get_sensor_state;
- break;
- }
- case RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE:
- {
- struct retro_camera_callback *cb =
- (struct retro_camera_callback*)data;
- camera_driver_state_t *camera_st = camera_state_get_ptr();
- RARCH_LOG("[Environ]: GET_CAMERA_INTERFACE.\n");
- cb->start = driver_camera_start;
- cb->stop = driver_camera_stop;
- camera_st->cb = *cb;
- camera_st->active = (cb->caps != 0);
- break;
- }
- case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE:
- {
- struct retro_location_callback *cb =
- (struct retro_location_callback*)data;
- location_driver_state_t
- *location_st = location_state_get_ptr();
- RARCH_LOG("[Environ]: GET_LOCATION_INTERFACE.\n");
- cb->start = driver_location_start;
- cb->stop = driver_location_stop;
- cb->get_position = driver_location_get_position;
- cb->set_interval = driver_location_set_interval;
- if (system)
- system->location_cb = *cb;
- location_st->active = false;
- break;
- }
- case RETRO_ENVIRONMENT_GET_LOG_INTERFACE:
- {
- struct retro_log_callback *cb = (struct retro_log_callback*)data;
- RARCH_LOG("[Environ]: GET_LOG_INTERFACE.\n");
- cb->log = libretro_log_cb;
- break;
- }
- case RETRO_ENVIRONMENT_GET_PERF_INTERFACE:
- {
- struct retro_perf_callback *cb = (struct retro_perf_callback*)data;
- RARCH_LOG("[Environ]: GET_PERF_INTERFACE.\n");
- cb->get_time_usec = cpu_features_get_time_usec;
- cb->get_cpu_features = cpu_features_get;
- cb->get_perf_counter = cpu_features_get_perf_counter;
- cb->perf_register = runloop_performance_counter_register;
- cb->perf_start = core_performance_counter_start;
- cb->perf_stop = core_performance_counter_stop;
- cb->perf_log = runloop_perf_log;
- break;
- }
- case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY:
- {
- const char **dir = (const char**)data;
- const char *dir_core_assets = settings->paths.directory_core_assets;
- *dir = *dir_core_assets ? dir_core_assets : NULL;
- RARCH_LOG("[Environ]: CORE_ASSETS_DIRECTORY: \"%s\".\n",
- dir_core_assets);
- break;
- }
- case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO:
- /**
- * Update the system Audio/Video information.
- * Will reinitialize audio/video drivers if needed.
- * Used by RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO.
- **/
- {
- const struct retro_system_av_info **info = (const struct retro_system_av_info**)&data;
- video_driver_state_t *video_st = video_state_get_ptr();
- struct retro_system_av_info *av_info = &video_st->av_info;
- if (data)
- {
- int reinit_flags = DRIVERS_CMD_ALL;
- settings_t *settings = config_get_ptr();
- float refresh_rate = (*info)->timing.fps;
- unsigned crt_switch_resolution = settings->uints.crt_switch_resolution;
- bool video_fullscreen = settings->bools.video_fullscreen;
- bool video_switch_refresh_rate = false;
- bool no_video_reinit = true;
- /* Refresh rate switch for regular displays */
- if (video_display_server_has_resolution_list())
- video_switch_refresh_rate_maybe(&refresh_rate, &video_switch_refresh_rate);
- no_video_reinit = (
- crt_switch_resolution == 0
- && video_switch_refresh_rate == false
- && data
- && ((*info)->geometry.max_width == av_info->geometry.max_width)
- && ((*info)->geometry.max_height == av_info->geometry.max_height));
- /* First set new refresh rate and display rate, then after REINIT do
- * another display rate change to make sure the change stays */
- if (video_switch_refresh_rate && video_display_server_set_refresh_rate(refresh_rate))
- video_monitor_set_refresh_rate(refresh_rate);
- /* When not doing video reinit, we also must not do input and menu
- * reinit, otherwise the input driver crashes and the menu gets
- * corrupted. */
- if (no_video_reinit)
- reinit_flags =
- DRIVERS_CMD_ALL &
- ~(DRIVER_VIDEO_MASK | DRIVER_INPUT_MASK |
- DRIVER_MENU_MASK);
- RARCH_LOG("[Environ]: SET_SYSTEM_AV_INFO: %ux%u, Aspect: %.3f, FPS: %.2f, Sample rate: %.2f Hz.\n",
- (*info)->geometry.base_width, (*info)->geometry.base_height,
- (*info)->geometry.aspect_ratio,
- (*info)->timing.fps,
- (*info)->timing.sample_rate);
- memcpy(av_info, *info, sizeof(*av_info));
- video_st->core_frame_time = 1000000 /
- ((video_st->av_info.timing.fps > 0.0) ?
- video_st->av_info.timing.fps : 60.0);
- command_event(CMD_EVENT_REINIT, &reinit_flags);
- if (no_video_reinit)
- video_driver_set_aspect_ratio();
- if (video_switch_refresh_rate)
- video_display_server_set_refresh_rate(refresh_rate);
- /* Cannot continue recording with different parameters.
- * Take the easiest route out and just restart
- * the recording. */
- if (recording_st->data)
- {
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT),
- 2, 180, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- command_event(CMD_EVENT_RECORD_DEINIT, NULL);
- command_event(CMD_EVENT_RECORD_INIT, NULL);
- }
- /* Hide mouse cursor in fullscreen after
- * a RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO call. */
- if (video_fullscreen)
- video_driver_hide_mouse();
- /* Recalibrate frame delay target when video reinits
- * and pause frame delay when video does not reinit */
- if (settings->bools.video_frame_delay_auto)
- {
- if (no_video_reinit)
- video_st->frame_delay_pause = true;
- else
- video_st->frame_delay_target = 0;
- }
- return true;
- }
- return false;
- }
- case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO:
- {
- unsigned i;
- const struct retro_subsystem_info *info =
- (const struct retro_subsystem_info*)data;
- unsigned log_level = settings->uints.libretro_log_level;
- RARCH_DBG("[Environ]: SET_SUBSYSTEM_INFO.\n");
- for (i = 0; info[i].ident; i++)
- {
- unsigned j;
- if (log_level != RETRO_LOG_DEBUG)
- continue;
- RARCH_DBG("Special game type: %s\n Ident: %s\n ID: %u\n Content:\n",
- info[i].desc,
- info[i].ident,
- info[i].id
- );
- for (j = 0; j < info[i].num_roms; j++)
- {
- RARCH_DBG(" %s (%s)\n",
- info[i].roms[j].desc, info[i].roms[j].required ?
- "required" : "optional");
- }
- }
- if (system)
- {
- struct retro_subsystem_info *info_ptr = NULL;
- free(system->subsystem.data);
- system->subsystem.data = NULL;
- system->subsystem.size = 0;
- info_ptr = (struct retro_subsystem_info*)
- malloc(i * sizeof(*info_ptr));
- if (!info_ptr)
- return false;
- system->subsystem.data = info_ptr;
- memcpy(system->subsystem.data, info,
- i * sizeof(*system->subsystem.data));
- system->subsystem.size = i;
- runloop_st->current_core.flags |=
- RETRO_CORE_FLAG_HAS_SET_SUBSYSTEMS;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO:
- {
- unsigned i, j;
- const struct retro_controller_info *info
- = (const struct retro_controller_info*)data;
- unsigned log_level = settings->uints.libretro_log_level;
- RARCH_LOG("[Environ]: SET_CONTROLLER_INFO.\n");
- for (i = 0; info[i].types; i++)
- {
- if (log_level != RETRO_LOG_DEBUG)
- continue;
- RARCH_DBG(" Controller port: %u\n", i + 1);
- for (j = 0; j < info[i].num_types; j++)
- RARCH_DBG(" %s (ID: %u)\n", info[i].types[j].desc,
- info[i].types[j].id);
- }
- if (system)
- {
- struct retro_controller_info *info_ptr = NULL;
- free(system->ports.data);
- system->ports.data = NULL;
- system->ports.size = 0;
- info_ptr = (struct retro_controller_info*)calloc(i, sizeof(*info_ptr));
- if (!info_ptr)
- return false;
- system->ports.data = info_ptr;
- memcpy(system->ports.data, info,
- i * sizeof(*system->ports.data));
- system->ports.size = i;
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_MEMORY_MAPS:
- {
- if (system)
- {
- unsigned i;
- const struct retro_memory_map *mmaps =
- (const struct retro_memory_map*)data;
- rarch_memory_descriptor_t *descriptors = NULL;
- unsigned int log_level = settings->uints.libretro_log_level;
- RARCH_LOG("[Environ]: SET_MEMORY_MAPS.\n");
- free((void*)system->mmaps.descriptors);
- system->mmaps.descriptors = 0;
- system->mmaps.num_descriptors = 0;
- descriptors = (rarch_memory_descriptor_t*)calloc(mmaps->num_descriptors,
- sizeof(*descriptors));
- if (!descriptors)
- return false;
- system->mmaps.descriptors = descriptors;
- system->mmaps.num_descriptors = mmaps->num_descriptors;
- for (i = 0; i < mmaps->num_descriptors; i++)
- system->mmaps.descriptors[i].core = mmaps->descriptors[i];
- mmap_preprocess_descriptors(descriptors, mmaps->num_descriptors);
- if (log_level != RETRO_LOG_DEBUG)
- break;
- if (sizeof(void *) == 8)
- RARCH_DBG(" ndx flags ptr offset start select disconn len addrspace\n");
- else
- RARCH_DBG(" ndx flags ptr offset start select disconn len addrspace\n");
- for (i = 0; i < system->mmaps.num_descriptors; i++)
- {
- const rarch_memory_descriptor_t *desc =
- &system->mmaps.descriptors[i];
- char flags[7];
- flags[0] = 'M';
- if ((desc->core.flags & RETRO_MEMDESC_MINSIZE_8) == RETRO_MEMDESC_MINSIZE_8)
- flags[1] = '8';
- else if ((desc->core.flags & RETRO_MEMDESC_MINSIZE_4) == RETRO_MEMDESC_MINSIZE_4)
- flags[1] = '4';
- else if ((desc->core.flags & RETRO_MEMDESC_MINSIZE_2) == RETRO_MEMDESC_MINSIZE_2)
- flags[1] = '2';
- else
- flags[1] = '1';
- flags[2] = 'A';
- if ((desc->core.flags & RETRO_MEMDESC_ALIGN_8) == RETRO_MEMDESC_ALIGN_8)
- flags[3] = '8';
- else if ((desc->core.flags & RETRO_MEMDESC_ALIGN_4) == RETRO_MEMDESC_ALIGN_4)
- flags[3] = '4';
- else if ((desc->core.flags & RETRO_MEMDESC_ALIGN_2) == RETRO_MEMDESC_ALIGN_2)
- flags[3] = '2';
- else
- flags[3] = '1';
- flags[4] = (desc->core.flags & RETRO_MEMDESC_BIGENDIAN) ? 'B' : 'b';
- flags[5] = (desc->core.flags & RETRO_MEMDESC_CONST) ? 'C' : 'c';
- flags[6] = 0;
- RARCH_DBG(" %03u %s %p %08X %08X %08X %08X %08X %s\n",
- i + 1, flags, desc->core.ptr, desc->core.offset, desc->core.start,
- desc->core.select, desc->core.disconnect, desc->core.len,
- desc->core.addrspace ? desc->core.addrspace : "");
- }
- }
- else
- {
- RARCH_WARN("[Environ]: SET_MEMORY_MAPS, but system pointer not initialized..\n");
- }
- break;
- }
- case RETRO_ENVIRONMENT_SET_GEOMETRY:
- {
- video_driver_state_t *video_st = video_state_get_ptr();
- struct retro_system_av_info *av_info = &video_st->av_info;
- struct retro_game_geometry *geom = (struct retro_game_geometry*)&av_info->geometry;
- const struct retro_game_geometry *in_geom= (const struct retro_game_geometry*)data;
- if (!geom)
- return false;
- /* Can potentially be called every frame,
- * don't do anything unless required. */
- if ( (geom->base_width != in_geom->base_width) ||
- (geom->base_height != in_geom->base_height) ||
- (geom->aspect_ratio != in_geom->aspect_ratio))
- {
- geom->base_width = in_geom->base_width;
- geom->base_height = in_geom->base_height;
- geom->aspect_ratio = in_geom->aspect_ratio;
- RARCH_LOG("[Environ]: SET_GEOMETRY: %ux%u, Aspect: %.3f.\n",
- geom->base_width, geom->base_height, geom->aspect_ratio);
- /* Forces recomputation of aspect ratios if
- * using core-dependent aspect ratios. */
- video_driver_set_aspect_ratio();
- /* Ignore frame delay target temporarily */
- if (settings->bools.video_frame_delay_auto)
- video_st->frame_delay_pause = true;
- /* TODO: Figure out what to do, if anything, with
- recording. */
- }
- else
- {
- RARCH_LOG("[Environ]: SET_GEOMETRY.\n");
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER:
- {
- video_driver_state_t *video_st = video_state_get_ptr();
- struct retro_framebuffer *fb = (struct retro_framebuffer*)data;
- if (
- video_st->poke
- && video_st->poke->get_current_software_framebuffer
- && video_st->poke->get_current_software_framebuffer(
- video_st->data, fb))
- return true;
- return false;
- }
- case RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE:
- {
- video_driver_state_t *video_st = video_state_get_ptr();
- const struct retro_hw_render_interface **iface = (const struct retro_hw_render_interface **)data;
- if (
- video_st->poke
- && video_st->poke->get_hw_render_interface
- && video_st->poke->get_hw_render_interface(
- video_st->data, iface))
- return true;
- return false;
- }
- case RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS:
- #ifdef HAVE_CHEEVOS
- {
- bool state = *(const bool*)data;
- RARCH_LOG("[Environ]: SET_SUPPORT_ACHIEVEMENTS: %s.\n", state ? "yes" : "no");
- rcheevos_set_support_cheevos(state);
- }
- #endif
- break;
- case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE:
- {
- video_driver_state_t *video_st = video_state_get_ptr();
- const struct retro_hw_render_context_negotiation_interface *iface =
- (const struct retro_hw_render_context_negotiation_interface*)data;
- RARCH_LOG("[Environ]: SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE.\n");
- video_st->hw_render_context_negotiation = iface;
- break;
- }
- case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS:
- {
- uint64_t *quirks = (uint64_t *) data;
- RARCH_LOG("[Environ]: SET_SERIALIZATION_QUIRKS.\n");
- runloop_st->current_core.serialization_quirks_v = *quirks;
- break;
- }
- case RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT:
- #ifdef HAVE_LIBNX
- RARCH_LOG("[Environ]: SET_HW_SHARED_CONTEXT - ignored for now.\n");
- /* TODO/FIXME - Force this off for now for Switch
- * until shared HW context can work there */
- return false;
- #else
- RARCH_LOG("[Environ]: SET_HW_SHARED_CONTEXT.\n");
- runloop_st->flags |= RUNLOOP_FLAG_CORE_SET_SHARED_CONTEXT;
- #endif
- break;
- case RETRO_ENVIRONMENT_GET_VFS_INTERFACE:
- {
- const uint32_t supported_vfs_version = 3;
- static struct retro_vfs_interface vfs_iface =
- {
- /* VFS API v1 */
- retro_vfs_file_get_path_impl,
- retro_vfs_file_open_impl,
- retro_vfs_file_close_impl,
- retro_vfs_file_size_impl,
- retro_vfs_file_tell_impl,
- retro_vfs_file_seek_impl,
- retro_vfs_file_read_impl,
- retro_vfs_file_write_impl,
- retro_vfs_file_flush_impl,
- retro_vfs_file_remove_impl,
- retro_vfs_file_rename_impl,
- /* VFS API v2 */
- retro_vfs_file_truncate_impl,
- /* VFS API v3 */
- retro_vfs_stat_impl,
- retro_vfs_mkdir_impl,
- retro_vfs_opendir_impl,
- retro_vfs_readdir_impl,
- retro_vfs_dirent_get_name_impl,
- retro_vfs_dirent_is_dir_impl,
- retro_vfs_closedir_impl
- };
- struct retro_vfs_interface_info *vfs_iface_info = (struct retro_vfs_interface_info *) data;
- if (vfs_iface_info->required_interface_version <= supported_vfs_version)
- {
- RARCH_LOG("[Environ]: GET_VFS_INTERFACE. Core requested version >= V%d, providing V%d.\n",
- vfs_iface_info->required_interface_version, supported_vfs_version);
- vfs_iface_info->required_interface_version = supported_vfs_version;
- vfs_iface_info->iface = &vfs_iface;
- system->supports_vfs = true;
- }
- else
- {
- RARCH_WARN("[Environ]: GET_VFS_INTERFACE. Core requested version V%d which is higher than what we support (V%d).\n",
- vfs_iface_info->required_interface_version, supported_vfs_version);
- return false;
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_LED_INTERFACE:
- {
- struct retro_led_interface *ledintf = (struct retro_led_interface *)data;
- if (ledintf)
- ledintf->set_led_state = led_driver_set_led;
- RARCH_LOG("[Environ]: GET_LED_INTERFACE.\n");
- break;
- }
- case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE:
- {
- int result = 0;
- video_driver_state_t *video_st = video_state_get_ptr();
- audio_driver_state_t *audio_st = audio_state_get_ptr();
- if ( !(audio_st->flags & AUDIO_FLAG_SUSPENDED)
- && (audio_st->flags & AUDIO_FLAG_ACTIVE))
- result |= 2;
- if ( (video_st->flags & VIDEO_FLAG_ACTIVE)
- && !(video_st->current_video->frame == video_null.frame))
- result |= 1;
- #ifdef HAVE_RUNAHEAD
- if (audio_st->flags & AUDIO_FLAG_HARD_DISABLE)
- result |= 8;
- #endif
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_REPLAYING, NULL))
- result &= ~(1|2);
- #endif
- #if defined(HAVE_RUNAHEAD) || defined(HAVE_NETWORKING)
- /* Deprecated.
- Use RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT instead. */
- /* TODO/FIXME: Get rid of this ugly hack. */
- if (runloop_st->flags & RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE)
- result |= 4;
- #endif
- if (data)
- {
- int* result_p = (int*)data;
- *result_p = result;
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT:
- {
- int result = RETRO_SAVESTATE_CONTEXT_NORMAL;
- #if defined(HAVE_RUNAHEAD) || defined(HAVE_NETWORKING)
- if (runloop_st->flags & RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE)
- {
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- result = RETRO_SAVESTATE_CONTEXT_ROLLBACK_NETPLAY;
- else
- #endif
- {
- #ifdef HAVE_RUNAHEAD
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- settings_t *settings = config_get_ptr();
- if ( settings->bools.run_ahead_secondary_instance
- && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE)
- && secondary_core_ensure_exists(runloop_st, settings))
- result = RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY;
- else
- #endif
- result = RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE;
- #endif
- }
- }
- #endif
- if (data)
- *(int*)data = result;
- break;
- }
- case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE:
- {
- struct retro_midi_interface *midi_interface =
- (struct retro_midi_interface *)data;
- if (midi_interface)
- {
- midi_interface->input_enabled = midi_driver_input_enabled;
- midi_interface->output_enabled = midi_driver_output_enabled;
- midi_interface->read = midi_driver_read;
- midi_interface->write = midi_driver_write;
- midi_interface->flush = midi_driver_flush;
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_FASTFORWARDING:
- *(bool *)data = ((runloop_st->flags & RUNLOOP_FLAG_FASTMOTION) > 0);
- break;
- case RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE:
- {
- struct retro_fastforwarding_override *fastforwarding_override =
- (struct retro_fastforwarding_override *)data;
- /* Record new retro_fastforwarding_override parameters
- * and schedule application on the the next call of
- * runloop_check_state() */
- if (fastforwarding_override)
- {
- memcpy(&runloop_st->fastmotion_override.next,
- fastforwarding_override,
- sizeof(runloop_st->fastmotion_override.next));
- runloop_st->fastmotion_override.pending = true;
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_THROTTLE_STATE:
- {
- video_driver_state_t *video_st = video_state_get_ptr();
- audio_driver_state_t *audio_st = audio_state_get_ptr();
- struct retro_throttle_state *throttle_state
- = (struct retro_throttle_state *)data;
- bool menu_opened = false;
- bool core_paused = runloop_st->flags & RUNLOOP_FLAG_PAUSED;
- bool no_audio = ((audio_st->flags & AUDIO_FLAG_SUSPENDED)
- || !(audio_st->flags & AUDIO_FLAG_ACTIVE));
- float core_fps = (float)video_st->av_info.timing.fps;
- #ifdef HAVE_REWIND
- if (runloop_st->rewind_st.flags
- & STATE_MGR_REWIND_ST_FLAG_FRAME_IS_REVERSED)
- {
- throttle_state->mode = RETRO_THROTTLE_REWINDING;
- throttle_state->rate = 0.0f;
- break; /* ignore vsync */
- }
- #endif
- #ifdef HAVE_MENU
- menu_opened = menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE;
- if (menu_opened)
- #ifdef HAVE_NETWORKING
- core_paused = settings->bools.menu_pause_libretro &&
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_PAUSE, NULL);
- #else
- core_paused = settings->bools.menu_pause_libretro;
- #endif
- #endif
- if (core_paused)
- {
- throttle_state->mode = RETRO_THROTTLE_FRAME_STEPPING;
- throttle_state->rate = 0.0f;
- break; /* ignore vsync */
- }
- /* Base mode and rate. */
- throttle_state->mode = RETRO_THROTTLE_NONE;
- throttle_state->rate = core_fps;
- if (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
- {
- throttle_state->mode = RETRO_THROTTLE_FAST_FORWARD;
- throttle_state->rate *= runloop_get_fastforward_ratio(
- settings, &runloop_st->fastmotion_override.current);
- }
- else if ((runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
- && !no_audio)
- {
- throttle_state->mode = RETRO_THROTTLE_SLOW_MOTION;
- throttle_state->rate /= (settings->floats.slowmotion_ratio > 0.0f ?
- settings->floats.slowmotion_ratio : 1.0f);
- }
- /* VSync overrides the mode if the rate is limited by the display. */
- if (menu_opened || /* Menu currently always runs with vsync on. */
- ( settings->bools.video_vsync
- && (!(runloop_st->flags & RUNLOOP_FLAG_FORCE_NONBLOCK))
- && !(input_state_get_ptr()->flags & INP_FLAG_NONBLOCKING)))
- {
- float refresh_rate = video_driver_get_refresh_rate();
- if (refresh_rate == 0.0f)
- refresh_rate = settings->floats.video_refresh_rate;
- if (refresh_rate < throttle_state->rate || !throttle_state->rate)
- {
- /* Keep the mode as fast forward even if vsync limits it. */
- if (refresh_rate < core_fps)
- throttle_state->mode = RETRO_THROTTLE_VSYNC;
- throttle_state->rate = refresh_rate;
- }
- }
- /* Special behavior while audio output is not available. */
- if (no_audio && throttle_state->mode != RETRO_THROTTLE_FAST_FORWARD
- && throttle_state->mode != RETRO_THROTTLE_VSYNC)
- {
- /* Keep base if frame limiter matching the core is active. */
- retro_time_t core_limit = (core_fps
- ? (retro_time_t)(1000000.0f / core_fps)
- : (retro_time_t)0);
- retro_time_t frame_limit = runloop_st->frame_limit_minimum_time;
- if (abs((int)(core_limit - frame_limit)) > 10)
- {
- throttle_state->mode = RETRO_THROTTLE_UNBLOCKED;
- throttle_state->rate = 0.0f;
- }
- }
- break;
- }
- case RETRO_ENVIRONMENT_GET_INPUT_BITMASKS:
- /* Just falldown, the function will return true */
- break;
- case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION:
- RARCH_LOG("[Environ]: GET_CORE_OPTIONS_VERSION.\n");
- /* Current API version is 2 */
- *(unsigned *)data = 2;
- break;
- case RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE:
- {
- /* Try to use the polled refresh rate first. */
- float target_refresh_rate = video_driver_get_refresh_rate();
- /* If the above function failed [possibly because it is not
- * implemented], use the refresh rate set in the config instead. */
- if (target_refresh_rate == 0.0f)
- {
- if (settings)
- target_refresh_rate = settings->floats.video_refresh_rate;
- }
- *(float *)data = target_refresh_rate;
- break;
- }
- case RETRO_ENVIRONMENT_GET_INPUT_MAX_USERS:
- *(unsigned *)data = settings->uints.input_max_users;
- break;
- /* Private environment callbacks.
- *
- * Should all be properly addressed in version 2.
- * */
- case RETRO_ENVIRONMENT_POLL_TYPE_OVERRIDE:
- {
- const unsigned *poll_type_data = (const unsigned*)data;
- if (poll_type_data)
- runloop_st->core_poll_type_override = (enum poll_type_override_t)*poll_type_data;
- }
- break;
- case RETRO_ENVIRONMENT_GET_CLEAR_ALL_THREAD_WAITS_CB:
- *(retro_environment_t *)data = runloop_clear_all_thread_waits;
- break;
- case RETRO_ENVIRONMENT_SET_SAVE_STATE_IN_BACKGROUND:
- {
- bool state = *(const bool*)data;
- RARCH_LOG("[Environ]: SET_SAVE_STATE_IN_BACKGROUND: %s.\n", state ? "yes" : "no");
- set_save_state_in_background(state);
- }
- break;
- case RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE:
- {
- const struct retro_system_content_info_override *overrides =
- (const struct retro_system_content_info_override *)data;
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE.\n");
- /* Passing NULL always results in 'success' - this
- * allows cores to test for frontend support of
- * the RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE and
- * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT callbacks */
- if (!overrides)
- return true;
- return content_file_override_set(overrides);
- }
- break;
- case RETRO_ENVIRONMENT_GET_GAME_INFO_EXT:
- {
- content_state_t *p_content =
- content_state_get_ptr();
- const struct retro_game_info_ext **game_info_ext =
- (const struct retro_game_info_ext **)data;
- RARCH_LOG("[Environ]: RETRO_ENVIRONMENT_GET_GAME_INFO_EXT.\n");
- if (!game_info_ext)
- return false;
- if ( p_content
- && p_content->content_list
- && p_content->content_list->game_info_ext)
- *game_info_ext = p_content->content_list->game_info_ext;
- else
- {
- RARCH_ERR("[Environ]: Failed to retrieve extended game info.\n");
- *game_info_ext = NULL;
- return false;
- }
- }
- break;
- case RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT:
- {
- struct retro_hw_render_context_negotiation_interface *iface =
- (struct retro_hw_render_context_negotiation_interface*)data;
- #ifdef HAVE_VULKAN
- if (iface->interface_type == RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN)
- iface->interface_version = RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION;
- else
- #endif
- {
- iface->interface_version = 0;
- }
- }
- break;
- case RETRO_ENVIRONMENT_GET_JIT_CAPABLE:
- {
- #if defined(HAVE_COCOATOUCH) && TARGET_OS_IOS
- *(bool*)data = jb_has_debugger_attached();
- #else
- *(bool*)data = true;
- #endif
- }
- break;
- default:
- RARCH_LOG("[Environ]: UNSUPPORTED (#%u).\n", cmd);
- return false;
- }
- return true;
- }
- bool libretro_get_system_info(
- const char *path,
- struct retro_system_info *info,
- bool *load_no_content)
- {
- struct retro_system_info dummy_info;
- #ifdef HAVE_DYNAMIC
- dylib_t lib;
- #endif
- runloop_state_t *runloop_st = &runloop_state;
- if (string_ends_with_size(path,
- "builtin", strlen(path), STRLEN_CONST("builtin")))
- return false;
- dummy_info.library_name = NULL;
- dummy_info.library_version = NULL;
- dummy_info.valid_extensions = NULL;
- dummy_info.need_fullpath = false;
- dummy_info.block_extract = false;
- #ifdef HAVE_DYNAMIC
- if (!(lib = libretro_get_system_info_lib(
- path, &dummy_info, load_no_content)))
- {
- RARCH_ERR("%s: \"%s\"\n",
- msg_hash_to_str(MSG_FAILED_TO_OPEN_LIBRETRO_CORE),
- path);
- RARCH_ERR("Error(s): %s\n", dylib_error());
- return false;
- }
- #else
- if (load_no_content)
- {
- runloop_st->load_no_content_hook = load_no_content;
- /* load_no_content gets set in this callback. */
- retro_set_environment(runloop_environ_cb_get_system_info);
- /* It's possible that we just set get_system_info callback
- * to the currently running core.
- *
- * Make sure we reset it to the actual environment callback.
- * Ignore any environment callbacks here in case we're running
- * on the non-current core. */
- runloop_st->flags |= RUNLOOP_FLAG_IGNORE_ENVIRONMENT_CB;
- retro_set_environment(runloop_environment_cb);
- runloop_st->flags &= ~RUNLOOP_FLAG_IGNORE_ENVIRONMENT_CB;
- }
- retro_get_system_info(&dummy_info);
- #endif
- memcpy(info, &dummy_info, sizeof(*info));
- runloop_st->current_library_name[0] = '\0';
- runloop_st->current_library_version[0] = '\0';
- runloop_st->current_valid_extensions[0] = '\0';
- if (!string_is_empty(dummy_info.library_name))
- strlcpy(runloop_st->current_library_name,
- dummy_info.library_name,
- sizeof(runloop_st->current_library_name));
- if (!string_is_empty(dummy_info.library_version))
- strlcpy(runloop_st->current_library_version,
- dummy_info.library_version,
- sizeof(runloop_st->current_library_version));
- if (dummy_info.valid_extensions)
- strlcpy(runloop_st->current_valid_extensions,
- dummy_info.valid_extensions,
- sizeof(runloop_st->current_valid_extensions));
- info->library_name = runloop_st->current_library_name;
- info->library_version = runloop_st->current_library_version;
- info->valid_extensions = runloop_st->current_valid_extensions;
- #ifdef HAVE_DYNAMIC
- dylib_close(lib);
- #endif
- return true;
- }
- bool runloop_init_libretro_symbols(
- void *data,
- enum rarch_core_type type,
- struct retro_core_t *current_core,
- const char *lib_path,
- void *_lib_handle_p)
- {
- #ifdef HAVE_DYNAMIC
- /* the library handle for use with the SYMBOL macro */
- dylib_t lib_handle_local;
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- #endif
- switch (type)
- {
- case CORE_TYPE_PLAIN:
- {
- #ifdef HAVE_DYNAMIC
- #ifdef HAVE_RUNAHEAD
- dylib_t *lib_handle_p = (dylib_t*)_lib_handle_p;
- if (!lib_path || !lib_handle_p)
- #endif
- {
- // bool core_loaded = false;
- const char *path = path_get(RARCH_PATH_CORE);
- // char full_path[PATH_MAX_LENGTH];
- // if (!task_push_core_restore(
- // "/storage/emulated/0/RetroArch/cores/mesen_libretro_android.so",
- // "/data/user/0/com.retroarch.aarch64/cores/",
- // &core_loaded)) {
- // RARCH_ERR("[Core]: Cannot open the dynamic libretro cores.");
- // return false;
- // } else {
- // fill_pathname_join_special(full_path, path, "mesen_libretro_android.so", sizeof(full_path));
- // path_set(RARCH_PATH_CORE, full_path);
- // path = "/data/user/0/com.retroarch.aarch64/cores/mesen_libretro_android.so";
- // }
- // path = path_get(RARCH_PATH_CORE);
- if (string_is_empty(path))
- {
- RARCH_ERR("[Core]: Frontend is built for dynamic libretro cores, but "
- "path is not set. Cannot continue.\n");
- retroarch_fail(1, "init_libretro_symbols()");
- }
- RARCH_LOG("[Core]: Loading dynamic libretro core from: \"%s\"\n",
- path);
- if (!(runloop_st->lib_handle = load_dynamic_core(
- path,
- path_get_ptr(RARCH_PATH_CORE),
- path_get_realsize(RARCH_PATH_CORE)
- )))
- {
- const char *failed_open_str =
- msg_hash_to_str(MSG_FAILED_TO_OPEN_LIBRETRO_CORE);
- RARCH_ERR("%s: \"%s\"\nError(s): %s\n", failed_open_str,
- path, dylib_error());
- runloop_msg_queue_push(failed_open_str,
- 1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- return false;
- }
- lib_handle_local = runloop_st->lib_handle;
- }
- #ifdef HAVE_RUNAHEAD
- else
- {
- /* for a secondary core, we already have a
- * primary library loaded, so we can skip
- * some checks and just load the library */
- lib_handle_local = dylib_load(lib_path);
- if (!lib_handle_local)
- return false;
- *lib_handle_p = lib_handle_local;
- }
- #endif
- #endif
- CORE_SYMBOLS(SYMBOL);
- }
- break;
- case CORE_TYPE_DUMMY:
- CORE_SYMBOLS(SYMBOL_DUMMY);
- break;
- case CORE_TYPE_FFMPEG:
- #ifdef HAVE_FFMPEG
- CORE_SYMBOLS(SYMBOL_FFMPEG);
- #endif
- break;
- case CORE_TYPE_MPV:
- #ifdef HAVE_MPV
- CORE_SYMBOLS(SYMBOL_MPV);
- #endif
- break;
- case CORE_TYPE_IMAGEVIEWER:
- #ifdef HAVE_IMAGEVIEWER
- CORE_SYMBOLS(SYMBOL_IMAGEVIEWER);
- #endif
- break;
- case CORE_TYPE_NETRETROPAD:
- #if defined(HAVE_NETWORKING) && defined(HAVE_NETWORKGAMEPAD)
- CORE_SYMBOLS(SYMBOL_NETRETROPAD);
- #endif
- break;
- case CORE_TYPE_VIDEO_PROCESSOR:
- #if defined(HAVE_VIDEOPROCESSOR)
- CORE_SYMBOLS(SYMBOL_VIDEOPROCESSOR);
- #endif
- break;
- }
- return true;
- }
- uint32_t runloop_get_flags(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- return runloop_st->flags;
- }
- void runloop_system_info_free(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- rarch_system_info_t *sys_info = &runloop_st->system;
- if (sys_info->subsystem.data)
- free(sys_info->subsystem.data);
- if (sys_info->ports.data)
- free(sys_info->ports.data);
- if (sys_info->mmaps.descriptors)
- free((void *)sys_info->mmaps.descriptors);
- sys_info->subsystem.data = NULL;
- sys_info->subsystem.size = 0;
- sys_info->ports.data = NULL;
- sys_info->ports.size = 0;
- sys_info->mmaps.descriptors = NULL;
- sys_info->mmaps.num_descriptors = 0;
- sys_info->info.library_name = NULL;
- sys_info->info.library_version = NULL;
- sys_info->info.valid_extensions = NULL;
- sys_info->info.need_fullpath = false;
- sys_info->info.block_extract = false;
- runloop_st->key_event = NULL;
- runloop_st->frontend_key_event = NULL;
- memset(&runloop_st->system, 0, sizeof(rarch_system_info_t));
- }
- static void runloop_frame_time_free(runloop_state_t *runloop_st)
- {
- memset(&runloop_st->frame_time, 0,
- sizeof(struct retro_frame_time_callback));
- runloop_st->frame_time_last = 0;
- runloop_st->max_frames = 0;
- }
- static void runloop_audio_buffer_status_free(runloop_state_t *runloop_st)
- {
- memset(&runloop_st->audio_buffer_status, 0,
- sizeof(struct retro_audio_buffer_status_callback));
- runloop_st->audio_latency = 0;
- }
- static void runloop_fastmotion_override_free(runloop_state_t *runloop_st)
- {
- video_driver_state_t
- *video_st = video_state_get_ptr();
- settings_t *settings = config_get_ptr();
- float fastforward_ratio = settings->floats.fastforward_ratio;
- bool reset_frame_limit = runloop_st->fastmotion_override.current.fastforward &&
- (runloop_st->fastmotion_override.current.ratio >= 0.0f) &&
- (runloop_st->fastmotion_override.current.ratio != fastforward_ratio);
- runloop_st->fastmotion_override.current.ratio = 0.0f;
- runloop_st->fastmotion_override.current.fastforward = false;
- runloop_st->fastmotion_override.current.notification = false;
- runloop_st->fastmotion_override.current.inhibit_toggle = false;
- runloop_st->fastmotion_override.next.ratio = 0.0f;
- runloop_st->fastmotion_override.next.fastforward = false;
- runloop_st->fastmotion_override.next.notification = false;
- runloop_st->fastmotion_override.next.inhibit_toggle = false;
- runloop_st->fastmotion_override.pending = false;
- if (reset_frame_limit)
- runloop_set_frame_limit(&video_st->av_info, fastforward_ratio);
- }
- void runloop_state_free(runloop_state_t *runloop_st)
- {
- runloop_frame_time_free(runloop_st);
- runloop_audio_buffer_status_free(runloop_st);
- input_game_focus_free();
- runloop_fastmotion_override_free(runloop_st);
- /* Only a single core options callback is used at present */
- runloop_st->core_options_callback.update_display = NULL;
- runloop_st->video_swap_interval_auto = 1;
- }
- /**
- * uninit_libretro_symbols:
- *
- * Frees libretro core.
- *
- * Frees all core options, associated state, and
- * unbinds all libretro callback symbols.
- **/
- static void uninit_libretro_symbols(
- struct retro_core_t *current_core)
- {
- runloop_state_t *runloop_st = &runloop_state;
- input_driver_state_t *input_st = input_state_get_ptr();
- audio_driver_state_t *audio_st = audio_state_get_ptr();
- camera_driver_state_t *camera_st = camera_state_get_ptr();
- location_driver_state_t *location_st = location_state_get_ptr();
- #ifdef HAVE_DYNAMIC
- if (runloop_st->lib_handle)
- dylib_close(runloop_st->lib_handle);
- runloop_st->lib_handle = NULL;
- #endif
- memset(current_core, 0, sizeof(struct retro_core_t));
- runloop_st->flags &= ~RUNLOOP_FLAG_CORE_SET_SHARED_CONTEXT;
- if (runloop_st->core_options)
- {
- runloop_deinit_core_options(
- runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE,
- path_get(RARCH_PATH_CORE_OPTIONS),
- runloop_st->core_options);
- runloop_st->flags &=
- ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- runloop_st->core_options = NULL;
- }
- runloop_system_info_free();
- audio_st->callback.callback = NULL;
- audio_st->callback.set_state = NULL;
- runloop_state_free(runloop_st);
- camera_st->active = false;
- location_st->active = false;
- /* Core has finished utilising the input driver;
- * reset 'analog input requested' flags */
- memset(&input_st->analog_requested, 0,
- sizeof(input_st->analog_requested));
- /* Performance counters no longer valid. */
- runloop_st->perf_ptr_libretro = 0;
- memset(runloop_st->perf_counters_libretro, 0,
- sizeof(runloop_st->perf_counters_libretro));
- }
- static retro_time_t runloop_core_runtime_tick(
- runloop_state_t *runloop_st,
- float slowmotion_ratio,
- retro_time_t current_time)
- {
- video_driver_state_t *video_st = video_state_get_ptr();
- retro_time_t frame_time =
- (1.0 / video_st->av_info.timing.fps) * 1000000;
- bool runloop_slowmotion = runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION;
- bool runloop_fastmotion = runloop_st->flags & RUNLOOP_FLAG_FASTMOTION;
- /* Account for slow motion */
- if (runloop_slowmotion)
- return (retro_time_t)((double)frame_time * slowmotion_ratio);
- /* Account for fast forward */
- if (runloop_fastmotion)
- {
- /* Doing it this way means we miss the first frame after
- * turning fast forward on, but it saves the overhead of
- * having to do:
- * retro_time_t current_usec = cpu_features_get_time_usec();
- * core_runtime_last = current_usec;
- * every frame when fast forward is off. */
- retro_time_t current_usec = current_time;
- retro_time_t potential_frame_time = current_usec -
- runloop_st->core_runtime_last;
- runloop_st->core_runtime_last = current_usec;
- if (potential_frame_time < frame_time)
- return potential_frame_time;
- }
- return frame_time;
- }
- static bool core_unload_game(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- video_driver_free_hw_context();
- video_driver_set_cached_frame_ptr(NULL);
- if ((runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED))
- {
- RARCH_LOG("[Core]: Unloading game..\n");
- runloop_st->current_core.retro_unload_game();
- runloop_st->core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE;
- runloop_st->current_core.flags &= ~RETRO_CORE_FLAG_GAME_LOADED;
- }
- audio_driver_stop();
- return true;
- }
- static void runloop_apply_fastmotion_override(runloop_state_t *runloop_st, settings_t *settings)
- {
- float fastforward_ratio_current;
- video_driver_state_t *video_st = video_state_get_ptr();
- bool frame_time_counter_reset_after_fastforwarding = settings ?
- settings->bools.frame_time_counter_reset_after_fastforwarding : false;
- float fastforward_ratio_default = settings ?
- settings->floats.fastforward_ratio : 0.0f;
- float fastforward_ratio_last =
- (runloop_st->fastmotion_override.current.fastforward &&
- (runloop_st->fastmotion_override.current.ratio >= 0.0f)) ?
- runloop_st->fastmotion_override.current.ratio :
- fastforward_ratio_default;
- #if defined(HAVE_GFX_WIDGETS)
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- #endif
- memcpy(&runloop_st->fastmotion_override.current,
- &runloop_st->fastmotion_override.next,
- sizeof(runloop_st->fastmotion_override.current));
- /* Check if 'fastmotion' state has changed */
- if (((runloop_st->flags & RUNLOOP_FLAG_FASTMOTION) > 0) !=
- runloop_st->fastmotion_override.current.fastforward)
- {
- input_driver_state_t *input_st = input_state_get_ptr();
- if (runloop_st->fastmotion_override.current.fastforward)
- runloop_st->flags |= RUNLOOP_FLAG_FASTMOTION;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_FASTMOTION;
- if (input_st)
- {
- if (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
- input_st->flags |= INP_FLAG_NONBLOCKING;
- else
- input_st->flags &= ~INP_FLAG_NONBLOCKING;
- }
- if (!(runloop_st->flags & RUNLOOP_FLAG_FASTMOTION))
- runloop_st->fastforward_after_frames = 1;
- driver_set_nonblock_state();
- /* Reset frame time counter when toggling
- * fast-forward off, if required */
- if ( !(runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
- && frame_time_counter_reset_after_fastforwarding)
- video_st->frame_time_count = 0;
- /* Ensure fast forward widget is disabled when
- * toggling fast-forward off
- * (required if RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE
- * is called during core de-initialisation) */
- #if defined(HAVE_GFX_WIDGETS)
- if ( p_dispwidget->active
- && !(runloop_st->flags & RUNLOOP_FLAG_FASTMOTION))
- video_st->flags &= ~VIDEO_FLAG_WIDGETS_FAST_FORWARD;
- #endif
- }
- /* Update frame limit, if required */
- fastforward_ratio_current = (runloop_st->fastmotion_override.current.fastforward &&
- (runloop_st->fastmotion_override.current.ratio >= 0.0f)) ?
- runloop_st->fastmotion_override.current.ratio :
- fastforward_ratio_default;
- if (fastforward_ratio_current != fastforward_ratio_last)
- runloop_set_frame_limit(&video_st->av_info,
- fastforward_ratio_current);
- }
- void runloop_event_deinit_core(void)
- {
- video_driver_state_t
- *video_st = video_state_get_ptr();
- runloop_state_t *runloop_st = &runloop_state;
- settings_t *settings = config_get_ptr();
- core_unload_game();
- video_driver_set_cached_frame_ptr(NULL);
- if (runloop_st->current_core.flags & RETRO_CORE_FLAG_INITED)
- {
- RARCH_LOG("[Core]: Unloading core..\n");
- runloop_st->current_core.retro_deinit();
- }
- /* retro_deinit() may call
- * RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE
- * (i.e. to ensure that fastforwarding is
- * disabled on core close)
- * > Check for any pending updates */
- if (runloop_st->fastmotion_override.pending)
- {
- runloop_apply_fastmotion_override(runloop_st,
- settings);
- runloop_st->fastmotion_override.pending = false;
- }
- if ( (runloop_st->flags & RUNLOOP_FLAG_REMAPS_CORE_ACTIVE)
- || (runloop_st->flags & RUNLOOP_FLAG_REMAPS_CONTENT_DIR_ACTIVE)
- || (runloop_st->flags & RUNLOOP_FLAG_REMAPS_GAME_ACTIVE)
- || !string_is_empty(runloop_st->name.remapfile)
- )
- {
- input_remapping_deinit(settings->bools.remap_save_on_exit);
- input_remapping_set_defaults(true);
- }
- else
- input_remapping_restore_global_config(true);
- RARCH_LOG("[Core]: Unloading core symbols..\n");
- uninit_libretro_symbols(&runloop_st->current_core);
- runloop_st->current_core.flags &= ~RETRO_CORE_FLAG_SYMBOLS_INITED;
- /* Restore original refresh rate, if it has been changed
- * automatically in SET_SYSTEM_AV_INFO */
- if (video_st->video_refresh_rate_original)
- {
- /* Set the av_info fps also to the original refresh rate */
- /* to avoid re-initialization problems */
- struct retro_system_av_info *av_info = &video_st->av_info;
- av_info->timing.fps = video_st->video_refresh_rate_original;
- video_display_server_restore_refresh_rate();
- }
- /* Recalibrate frame delay target */
- if (settings->bools.video_frame_delay_auto)
- video_st->frame_delay_target = 0;
- driver_uninit(DRIVERS_CMD_ALL);
- #ifdef HAVE_CONFIGFILE
- if (runloop_st->flags & RUNLOOP_FLAG_OVERRIDES_ACTIVE)
- {
- /* Reload the original config */
- config_unload_override();
- }
- #endif
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- runloop_st->runtime_shader_preset_path[0] = '\0';
- #endif
- }
- static bool runloop_path_init_subsystem(runloop_state_t *runloop_st)
- {
- unsigned i, j;
- const struct retro_subsystem_info *info = NULL;
- rarch_system_info_t *system = &runloop_st->system;
- bool subsystem_path_empty = path_is_empty(RARCH_PATH_SUBSYSTEM);
- const char *savefile_dir = runloop_st->savefile_dir;
- if (!system || subsystem_path_empty)
- return false;
- /* For subsystems, we know exactly which RAM types are supported. */
- /* We'll handle this error gracefully later. */
- if ((info = libretro_find_subsystem_info(
- system->subsystem.data,
- system->subsystem.size,
- path_get(RARCH_PATH_SUBSYSTEM))))
- {
- unsigned num_content = MIN(info->num_roms,
- subsystem_path_empty ?
- 0 : (unsigned)runloop_st->subsystem_fullpaths->size);
- for (i = 0; i < num_content; i++)
- {
- for (j = 0; j < info->roms[i].num_memory; j++)
- {
- char ext[32];
- union string_list_elem_attr attr;
- char savename[PATH_MAX_LENGTH];
- char path[PATH_MAX_LENGTH];
- const struct retro_subsystem_memory_info *mem =
- (const struct retro_subsystem_memory_info*)
- &info->roms[i].memory[j];
- ext[0] = '.';
- ext[1] = '\0';
- strlcat(ext, mem->extension, sizeof(ext));
- strlcpy(savename,
- runloop_st->subsystem_fullpaths->elems[i].data,
- sizeof(savename));
- path_remove_extension(savename);
- if (path_is_directory(savefile_dir))
- {
- /* Use SRAM dir */
- /* Redirect content fullpath to save directory. */
- strlcpy(path, savefile_dir, sizeof(path));
- fill_pathname_dir(path, savename, ext, sizeof(path));
- }
- else
- fill_pathname(path, savename, ext, sizeof(path));
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO),
- path);
- attr.i = mem->type;
- string_list_append((struct string_list*)savefile_ptr_get(),
- path, attr);
- }
- }
- }
- /* Let other relevant paths be inferred
- from the main SRAM location. */
- if (!retroarch_override_setting_is_set(
- RARCH_OVERRIDE_SETTING_SAVE_PATH, NULL))
- {
- size_t len = strlcpy(runloop_st->name.savefile,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.savefile));
- runloop_st->name.savefile[len ] = '.';
- runloop_st->name.savefile[len+1] = 's';
- runloop_st->name.savefile[len+2] = 'r';
- runloop_st->name.savefile[len+3] = 'm';
- runloop_st->name.savefile[len+4] = '\0';
- }
- if (path_is_directory(runloop_st->name.savefile))
- {
- fill_pathname_dir(runloop_st->name.savefile,
- runloop_st->runtime_content_path_basename,
- ".srm",
- sizeof(runloop_st->name.savefile));
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO),
- runloop_st->name.savefile);
- }
- return true;
- }
- static void runloop_path_init_savefile_internal(runloop_state_t *runloop_st)
- {
- path_deinit_savefile();
- path_init_savefile_new();
- if (!runloop_path_init_subsystem(runloop_st))
- path_init_savefile_rtc(runloop_st->name.savefile);
- }
- static void runloop_path_init_savefile(runloop_state_t *runloop_st)
- {
- bool should_sram_be_used =
- (runloop_st->flags & RUNLOOP_FLAG_USE_SRAM)
- && !(runloop_st->flags & RUNLOOP_FLAG_IS_SRAM_SAVE_DISABLED);
- if (should_sram_be_used)
- runloop_st->flags |= RUNLOOP_FLAG_USE_SRAM;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_USE_SRAM;
- if (!(runloop_st->flags & RUNLOOP_FLAG_USE_SRAM))
- {
- RARCH_LOG("[SRAM]: %s\n",
- msg_hash_to_str(MSG_SRAM_WILL_NOT_BE_SAVED));
- return;
- }
- command_event(CMD_EVENT_AUTOSAVE_INIT, NULL);
- }
- static bool event_init_content(
- runloop_state_t *runloop_st,
- settings_t *settings,
- input_driver_state_t *input_st)
- {
- #ifdef HAVE_CHEEVOS
- bool cheevos_enable =
- settings->bools.cheevos_enable;
- bool cheevos_hardcore_mode_enable =
- settings->bools.cheevos_hardcore_mode_enable;
- #endif
- const enum rarch_core_type current_core_type = runloop_st->current_core_type;
- uint8_t flags = content_get_flags();
- if (current_core_type == CORE_TYPE_PLAIN)
- runloop_st->flags |= RUNLOOP_FLAG_USE_SRAM;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_USE_SRAM;
- /* No content to be loaded for dummy core,
- * just successfully exit. */
- if (current_core_type == CORE_TYPE_DUMMY)
- return true;
- content_set_subsystem_info();
- /* If core is contentless, just initialise SRAM
- * interface, otherwise fill all content-related
- * paths */
- if (flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT)
- runloop_path_init_savefile_internal(runloop_st);
- else
- runloop_path_fill_names();
- if (!content_init())
- return false;
- command_event_set_savestate_auto_index(settings);
- command_event_set_replay_auto_index(settings);
- runloop_path_init_savefile(runloop_st);
- if (!event_load_save_files(runloop_st->flags &
- RUNLOOP_FLAG_IS_SRAM_LOAD_DISABLED))
- RARCH_LOG("[SRAM]: %s\n",
- msg_hash_to_str(MSG_SKIPPING_SRAM_LOAD));
- /*
- Since the operations are asynchronous we can't
- guarantee users will not use auto_load_state to cheat on
- achievements so we forbid auto_load_state from happening
- if cheevos_enable and cheevos_hardcode_mode_enable
- are true.
- */
- #ifdef HAVE_CHEEVOS
- if (!cheevos_enable || !cheevos_hardcore_mode_enable)
- #endif
- {
- #ifdef HAVE_BSV_MOVIE
- /* ignore entry state if we're doing bsv playback (we do want it
- for bsv recording though) */
- if (!(input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_PLAYBACK))
- #endif
- {
- if (runloop_st->entry_state_slot && !command_event_load_entry_state(settings))
- {
- /* loading the state failed, reset entry slot */
- runloop_st->entry_state_slot = 0;
- }
- }
- #ifdef HAVE_BSV_MOVIE
- /* ignore autoload state if we're doing bsv playback or recording */
- if (!(input_st->bsv_movie_state.flags & (BSV_FLAG_MOVIE_START_RECORDING | BSV_FLAG_MOVIE_START_PLAYBACK)))
- #endif
- {
- if (!runloop_st->entry_state_slot && settings->bools.savestate_auto_load)
- command_event_load_auto_state();
- }
- }
- #ifdef HAVE_BSV_MOVIE
- movie_stop(input_st);
- if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_RECORDING)
- {
- configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
- #ifndef HAVE_THREADS
- /* Hack: the regular scheduler doesn't do the right thing here at
- least in emscripten builds. I would expect that the check in
- task_movie.c:343 should defer recording until the movie task
- is done, but maybe that task isn't enqueued again yet when the
- movie-record task is checked? Or the finder call in
- content_load_state_in_progress is not correct? Either way,
- the load happens after the recording starts rather than the
- right way around.
- */
- task_queue_wait(NULL,NULL);
- #endif
- movie_start_record(input_st, input_st->bsv_movie_state.movie_start_path);
- }
- else if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_PLAYBACK)
- {
- configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
- movie_start_playback(input_st, input_st->bsv_movie_state.movie_start_path);
- }
- #endif
- command_event(CMD_EVENT_NETPLAY_INIT, NULL);
- return true;
- }
- static void runloop_runtime_log_init(runloop_state_t *runloop_st)
- {
- const char *content_path = path_get(RARCH_PATH_CONTENT);
- const char *core_path = path_get(RARCH_PATH_CORE);
- runloop_st->core_runtime_last = cpu_features_get_time_usec();
- runloop_st->core_runtime_usec = 0;
- /* Have to cache content and core path here, otherwise
- * logging fails if new content is loaded without
- * closing existing content
- * i.e. RARCH_PATH_CONTENT and RARCH_PATH_CORE get
- * updated when the new content is loaded, which
- * happens *before* command_event_runtime_log_deinit
- * -> using RARCH_PATH_CONTENT and RARCH_PATH_CORE
- * directly in command_event_runtime_log_deinit
- * can therefore lead to the runtime of the currently
- * loaded content getting written to the *new*
- * content's log file... */
- memset(runloop_st->runtime_content_path,
- 0, sizeof(runloop_st->runtime_content_path));
- memset(runloop_st->runtime_core_path,
- 0, sizeof(runloop_st->runtime_core_path));
- if (!string_is_empty(content_path))
- strlcpy(runloop_st->runtime_content_path,
- content_path,
- sizeof(runloop_st->runtime_content_path));
- if (!string_is_empty(core_path))
- strlcpy(runloop_st->runtime_core_path,
- core_path,
- sizeof(runloop_st->runtime_core_path));
- }
- void runloop_set_frame_limit(
- const struct retro_system_av_info *av_info,
- float fastforward_ratio)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (fastforward_ratio < 1.0f)
- runloop_st->frame_limit_minimum_time = 0.0f;
- else
- runloop_st->frame_limit_minimum_time = (retro_time_t)
- roundf(1000000.0f /
- (av_info->timing.fps * fastforward_ratio));
- }
- float runloop_get_fastforward_ratio(
- settings_t *settings,
- struct retro_fastforwarding_override *fastmotion_override)
- {
- if ( fastmotion_override->fastforward
- && (fastmotion_override->ratio >= 0.0f))
- return fastmotion_override->ratio;
- return settings->floats.fastforward_ratio;
- }
- void runloop_set_video_swap_interval(
- bool vrr_runloop_enable,
- bool crt_switching_active,
- unsigned swap_interval_config,
- float audio_max_timing_skew,
- float video_refresh_rate,
- double input_fps)
- {
- runloop_state_t *runloop_st = &runloop_state;
- float core_hz = input_fps;
- float timing_hz = crt_switching_active ?
- input_fps : video_refresh_rate;
- float swap_ratio;
- unsigned swap_integer;
- float timing_skew;
- /* If automatic swap interval selection is
- * disabled, just record user-set value */
- if (swap_interval_config != 0)
- {
- runloop_st->video_swap_interval_auto =
- swap_interval_config;
- return;
- }
- /* > If VRR is enabled, swap interval is irrelevant,
- * just set to 1
- * > If core fps is higher than display refresh rate,
- * set swap interval to 1
- * > If core fps or display refresh rate are zero,
- * set swap interval to 1 */
- if ( (vrr_runloop_enable)
- || (core_hz > timing_hz)
- || (core_hz <= 0.0f)
- || (timing_hz <= 0.0f))
- {
- runloop_st->video_swap_interval_auto = 1;
- return;
- }
- /* Check whether display refresh rate is an integer
- * multiple of core fps (within timing skew tolerance) */
- swap_ratio = timing_hz / core_hz;
- swap_integer = (unsigned)(swap_ratio + 0.5f);
- /* > Sanity check: swap interval must be in the
- * range [1,4] - if we are outside this, then
- * bail... */
- if ((swap_integer < 1) || (swap_integer > 4))
- {
- runloop_st->video_swap_interval_auto = 1;
- return;
- }
- timing_skew = fabs(1.0f - core_hz / (timing_hz / (float)swap_integer));
- runloop_st->video_swap_interval_auto =
- (timing_skew <= audio_max_timing_skew) ?
- swap_integer : 1;
- }
- unsigned runloop_get_video_swap_interval(
- unsigned swap_interval_config)
- {
- runloop_state_t *runloop_st = &runloop_state;
- return (swap_interval_config == 0) ?
- runloop_st->video_swap_interval_auto :
- swap_interval_config;
- }
- /*
- Returns rotation requested by the core regardless of if it has been
- applied with the final video rotation
- */
- unsigned int retroarch_get_core_requested_rotation(void)
- {
- return runloop_state.system.core_requested_rotation;
- }
- /*
- Returns final rotation including both user chosen video rotation
- and core requested rotation if allowed by video_allow_rotate
- */
- unsigned int retroarch_get_rotation(void)
- {
- settings_t *settings = config_get_ptr();
- return settings->uints.video_rotation + runloop_state.system.rotation;
- }
- static void retro_run_null(void) { } /* Stub function callback impl. */
- static bool core_verify_api_version(runloop_state_t *runloop_st)
- {
- unsigned api_version = runloop_st->current_core.retro_api_version();
- if (api_version != RETRO_API_VERSION)
- {
- RARCH_WARN("[Core]: %s\n", msg_hash_to_str(MSG_LIBRETRO_ABI_BREAK));
- return false;
- }
- RARCH_LOG("[Core]: %s: %u, %s: %u\n",
- msg_hash_to_str(MSG_VERSION_OF_LIBRETRO_API),
- api_version,
- msg_hash_to_str(MSG_COMPILED_AGAINST_API),
- RETRO_API_VERSION
- );
- return true;
- }
- static int16_t core_input_state_poll_late(unsigned port,
- unsigned device, unsigned idx, unsigned id)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!(runloop_st->current_core.flags & RETRO_CORE_FLAG_INPUT_POLLED))
- input_driver_poll();
- runloop_st->current_core.flags |= RETRO_CORE_FLAG_INPUT_POLLED;
- return input_driver_state_wrapper(port, device, idx, id);
- }
- static void core_input_state_poll_maybe(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- const enum poll_type_override_t
- core_poll_type_override = runloop_st->core_poll_type_override;
- unsigned new_poll_type = (core_poll_type_override > POLL_TYPE_OVERRIDE_DONTCARE)
- ? (core_poll_type_override - 1)
- : runloop_st->current_core.poll_type;
- if (new_poll_type == POLL_TYPE_NORMAL)
- input_driver_poll();
- }
- static retro_input_state_t core_input_state_poll_return_cb(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- const enum poll_type_override_t
- core_poll_type_override = runloop_st->core_poll_type_override;
- unsigned new_poll_type = (core_poll_type_override > POLL_TYPE_OVERRIDE_DONTCARE)
- ? (core_poll_type_override - 1)
- : runloop_st->current_core.poll_type;
- if (new_poll_type == POLL_TYPE_LATE)
- return core_input_state_poll_late;
- return input_driver_state_wrapper;
- }
- /**
- * core_init_libretro_cbs:
- * @data : pointer to retro_callbacks object
- *
- * Initializes libretro callbacks, and binds the libretro callbacks
- * to default callback functions.
- **/
- static void core_init_libretro_cbs(runloop_state_t *runloop_st,
- struct retro_callbacks *cbs)
- {
- retro_input_state_t state_cb = core_input_state_poll_return_cb();
- runloop_st->current_core.retro_set_video_refresh(video_driver_frame);
- runloop_st->current_core.retro_set_audio_sample(audio_driver_sample);
- runloop_st->current_core.retro_set_audio_sample_batch(audio_driver_sample_batch);
- runloop_st->current_core.retro_set_input_state(state_cb);
- runloop_st->current_core.retro_set_input_poll(core_input_state_poll_maybe);
- runloop_st->input_poll_callback_original = core_input_state_poll_maybe;
- core_set_default_callbacks(cbs);
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL))
- core_set_netplay_callbacks();
- #endif
- }
- static bool runloop_event_load_core(runloop_state_t *runloop_st,
- unsigned poll_type_behavior)
- {
- video_driver_state_t *video_st = video_state_get_ptr();
- runloop_st->current_core.poll_type = poll_type_behavior;
- if (!core_verify_api_version(runloop_st))
- return false;
- core_init_libretro_cbs(runloop_st, &runloop_st->retro_ctx);
- runloop_st->current_core.retro_get_system_av_info(&video_st->av_info);
- video_st->core_frame_time = 1000000 /
- ((video_st->av_info.timing.fps > 0.0) ?
- video_st->av_info.timing.fps : 60.0);
- return true;
- }
- bool runloop_event_init_core(
- settings_t *settings,
- void *input_data,
- enum rarch_core_type type,
- const char *old_savefile_dir,
- const char *old_savestate_dir)
- {
- size_t len;
- runloop_state_t *runloop_st = &runloop_state;
- input_driver_state_t *input_st = (input_driver_state_t*)input_data;
- video_driver_state_t *video_st = video_state_get_ptr();
- #ifdef HAVE_CONFIGFILE
- bool auto_overrides_enable = settings->bools.auto_overrides_enable;
- bool auto_remaps_enable = false;
- const char *dir_input_remapping = NULL;
- #endif
- bool show_set_initial_disk_msg = false;
- unsigned poll_type_behavior = 0;
- float fastforward_ratio = 0.0f;
- rarch_system_info_t *sys_info = &runloop_st->system;
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
- {
- #ifdef HAVE_UPDATE_CORES
- /* If netplay is enabled, update the core before initializing. */
- const char *path_core = path_get(RARCH_PATH_CORE);
- if (!string_is_empty(path_core) &&
- !string_is_equal(path_core, "builtin"))
- {
- if (task_push_update_single_core(path_core,
- settings->bools.core_updater_auto_backup,
- settings->uints.core_updater_auto_backup_history_size,
- settings->paths.directory_libretro,
- settings->paths.directory_core_assets))
- /* We must wait for the update to finish
- before starting the core. */
- task_queue_wait(NULL, NULL);
- }
- #endif
- /* We need this in order for core_info_current_supports_netplay
- to work correctly at init_netplay,
- called later at event_init_content. */
- command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
- command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
- }
- #endif
- /* Load symbols */
- if (!runloop_init_libretro_symbols(runloop_st,
- type, &runloop_st->current_core, NULL, NULL))
- return false;
- #ifdef HAVE_RUNAHEAD
- /* remember last core type created, so creating a
- * secondary core will know what core type to use. */
- runloop_st->last_core_type = type;
- #endif
- if (!runloop_st->current_core.retro_run)
- runloop_st->current_core.retro_run = retro_run_null;
- runloop_st->current_core.flags |= RETRO_CORE_FLAG_SYMBOLS_INITED;
- runloop_st->current_core.retro_get_system_info(&sys_info->info);
- if (!sys_info->info.library_name)
- sys_info->info.library_name = msg_hash_to_str(MSG_UNKNOWN);
- if (!sys_info->info.library_version)
- sys_info->info.library_version = "v0";
- len = strlcpy(
- video_st->title_buf,
- msg_hash_to_str(MSG_PROGRAM),
- sizeof(video_st->title_buf));
- video_st->title_buf[len ] = ' ';
- video_st->title_buf[len+1] = '\0';
- len = strlcat(video_st->title_buf,
- sys_info->info.library_name,
- sizeof(video_st->title_buf));
- video_st->title_buf[len ] = ' ';
- video_st->title_buf[len+1] = '\0';
- strlcat(video_st->title_buf,
- sys_info->info.library_version,
- sizeof(video_st->title_buf));
- strlcpy(sys_info->valid_extensions,
- sys_info->info.valid_extensions ?
- sys_info->info.valid_extensions : DEFAULT_EXT,
- sizeof(sys_info->valid_extensions));
- #ifdef HAVE_CONFIGFILE
- if (auto_overrides_enable)
- config_load_override(&runloop_st->system);
- #endif
- /* Cannot access these settings-related parameters
- * until *after* config overrides have been loaded */
- #ifdef HAVE_CONFIGFILE
- auto_remaps_enable = settings->bools.auto_remaps_enable;
- dir_input_remapping = settings->paths.directory_input_remapping;
- #endif
- show_set_initial_disk_msg = settings->bools.notification_show_set_initial_disk;
- poll_type_behavior = settings->uints.input_poll_type_behavior;
- fastforward_ratio = runloop_get_fastforward_ratio(
- settings, &runloop_st->fastmotion_override.current);
- #ifdef HAVE_CHEEVOS
- /* assume the core supports achievements unless it tells us otherwise */
- rcheevos_set_support_cheevos(true);
- #endif
- /* Load auto-shaders on the next occasion */
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- video_st->flags |= VIDEO_FLAG_SHADER_PRESETS_NEED_RELOAD;
- runloop_st->shader_delay_timer.timer_begin = false; /* not initialized */
- runloop_st->shader_delay_timer.timer_end = false; /* not expired */
- #endif
- /* reset video format to libretro's default */
- video_st->pix_fmt = RETRO_PIXEL_FORMAT_0RGB1555;
- runloop_st->current_core.retro_set_environment(runloop_environment_cb);
- /* Load any input remap files
- * > Note that we always cache the current global
- * input settings when initialising a core
- * (regardless of whether remap files are loaded)
- * so settings can be restored when the core is
- * unloaded - i.e. core remapping options modified
- * at runtime should not 'bleed through' into the
- * master config file */
- input_remapping_cache_global_config();
- #ifdef HAVE_CONFIGFILE
- if (auto_remaps_enable)
- config_load_remap(dir_input_remapping, &runloop_st->system);
- #endif
- /* Per-core saves: reset redirection paths */
- runloop_path_set_redirect(settings, old_savefile_dir, old_savestate_dir);
- video_driver_set_cached_frame_ptr(NULL);
- runloop_st->current_core.retro_init();
- runloop_st->current_core.flags |= RETRO_CORE_FLAG_INITED;
- /* Attempt to set initial disk index */
- disk_control_set_initial_index(
- &sys_info->disk_control,
- path_get(RARCH_PATH_CONTENT),
- runloop_st->savefile_dir);
- if (!event_init_content(runloop_st, settings, input_st))
- {
- runloop_st->flags &= ~RUNLOOP_FLAG_CORE_RUNNING;
- return false;
- }
- /* Verify that initial disk index was set correctly */
- disk_control_verify_initial_index(&sys_info->disk_control,
- show_set_initial_disk_msg);
- if (!runloop_event_load_core(runloop_st, poll_type_behavior))
- return false;
- runloop_set_frame_limit(&video_st->av_info, fastforward_ratio);
- runloop_st->frame_limit_last_time = cpu_features_get_time_usec();
- runloop_runtime_log_init(runloop_st);
- return true;
- }
- void runloop_pause_checks(void)
- {
- #ifdef HAVE_PRESENCE
- presence_userdata_t userdata;
- #endif
- runloop_state_t *runloop_st = &runloop_state;
- bool is_paused = runloop_st->flags & RUNLOOP_FLAG_PAUSED;
- bool is_idle = runloop_st->flags & RUNLOOP_FLAG_IDLE;
- #if defined(HAVE_GFX_WIDGETS)
- video_driver_state_t *video_st = video_state_get_ptr();
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- bool widgets_active = p_dispwidget->active;
- if (widgets_active)
- {
- if (is_paused)
- video_st->flags |= VIDEO_FLAG_WIDGETS_PAUSED;
- else
- video_st->flags &= ~VIDEO_FLAG_WIDGETS_PAUSED;
- }
- #endif
- if (is_paused)
- {
- #if defined(HAVE_GFX_WIDGETS)
- if (!widgets_active)
- #endif
- runloop_msg_queue_push(msg_hash_to_str(MSG_PAUSED), 1,
- 1, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (!is_idle)
- video_driver_cached_frame();
- #ifdef HAVE_PRESENCE
- userdata.status = PRESENCE_GAME_PAUSED;
- command_event(CMD_EVENT_PRESENCE_UPDATE, &userdata);
- #endif
- #ifndef HAVE_LAKKA_SWITCH
- #ifdef HAVE_LAKKA
- set_cpu_scaling_signal(CPUSCALING_EVENT_FOCUS_MENU);
- #endif
- #endif /* #ifndef HAVE_LAKKA_SWITCH */
- }
- else
- {
- #ifndef HAVE_LAKKA_SWITCH
- #ifdef HAVE_LAKKA
- set_cpu_scaling_signal(CPUSCALING_EVENT_FOCUS_CORE);
- #endif
- #endif /* #ifndef HAVE_LAKKA_SWITCH */
- }
- #if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS)
- if (p_dispwidget->ai_service_overlay_state == 1)
- gfx_widgets_ai_service_overlay_unload();
- #endif
- /* Signal/reset paused rewind to take the initial step */
- runloop_st->run_frames_and_pause = -1;
- }
- struct string_list *path_get_subsystem_list(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- return runloop_st->subsystem_fullpaths;
- }
- void runloop_path_fill_names(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- #ifdef HAVE_BSV_MOVIE
- input_driver_state_t *input_st = input_state_get_ptr();
- #endif
- runloop_path_init_savefile_internal(runloop_st);
- #ifdef HAVE_BSV_MOVIE
- strlcpy(input_st->bsv_movie_state.movie_auto_path,
- runloop_st->name.replay,
- sizeof(input_st->bsv_movie_state.movie_auto_path));
- #endif
- if (string_is_empty(runloop_st->runtime_content_path_basename))
- return;
- if (string_is_empty(runloop_st->name.ups))
- {
- size_t len = strlcpy(runloop_st->name.ups,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.ups));
- runloop_st->name.ups[len ] = '.';
- runloop_st->name.ups[len+1] = 'u';
- runloop_st->name.ups[len+2] = 'p';
- runloop_st->name.ups[len+3] = 's';
- runloop_st->name.ups[len+4] = '\0';
- }
- if (string_is_empty(runloop_st->name.bps))
- {
- size_t len = strlcpy(runloop_st->name.bps,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.bps));
- runloop_st->name.bps[len ] = '.';
- runloop_st->name.bps[len+1] = 'b';
- runloop_st->name.bps[len+2] = 'p';
- runloop_st->name.bps[len+3] = 's';
- runloop_st->name.bps[len+4] = '\0';
- }
- if (string_is_empty(runloop_st->name.ips))
- {
- size_t len = strlcpy(runloop_st->name.ips,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.ips));
- runloop_st->name.ips[len ] = '.';
- runloop_st->name.ips[len+1] = 'i';
- runloop_st->name.ips[len+2] = 'p';
- runloop_st->name.ips[len+3] = 's';
- runloop_st->name.ips[len+4] = '\0';
- }
- }
- /* Creates folder and core options stub file for subsequent runs */
- bool core_options_create_override(bool game_specific)
- {
- char options_path[PATH_MAX_LENGTH];
- runloop_state_t *runloop_st = &runloop_state;
- config_file_t *conf = NULL;
- options_path[0] = '\0';
- if (game_specific)
- {
- /* Get options file path (game-specific) */
- if (!validate_game_options(
- runloop_st->system.info.library_name,
- options_path,
- sizeof(options_path), true))
- goto error;
- }
- else
- {
- /* Sanity check - cannot create a folder-specific
- * override if a game-specific override is
- * already active */
- if (runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE)
- goto error;
- /* Get options file path (folder-specific) */
- if (!validate_folder_options(
- options_path,
- sizeof(options_path), true))
- goto error;
- }
- /* Open config file */
- if (!(conf = config_file_new_from_path_to_string(options_path)))
- if (!(conf = config_file_new_alloc()))
- goto error;
- /* Write config file */
- core_option_manager_flush(runloop_st->core_options, conf);
- if (!config_file_write(conf, options_path, true))
- goto error;
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_OPTIONS_FILE_CREATED_SUCCESSFULLY),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- path_set(RARCH_PATH_CORE_OPTIONS, options_path);
- if (game_specific)
- runloop_st->flags |= RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE;
- if (!game_specific)
- runloop_st->flags |= RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE;
- config_file_free(conf);
- return true;
- error:
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_ERROR_SAVING_CORE_OPTIONS_FILE),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (conf)
- config_file_free(conf);
- return false;
- }
- bool core_options_remove_override(bool game_specific)
- {
- char new_options_path[PATH_MAX_LENGTH];
- runloop_state_t *runloop_st = &runloop_state;
- settings_t *settings = config_get_ptr();
- core_option_manager_t *coreopts = runloop_st->core_options;
- bool per_core_options = !settings->bools.global_core_options;
- const char *path_core_options = settings->paths.path_core_options;
- const char *current_options_path = NULL;
- config_file_t *conf = NULL;
- bool folder_options_active = false;
- new_options_path[0] = '\0';
- /* Sanity check 1 - if there are no core options
- * or no overrides are active, there is nothing to do */
- if ( !coreopts ||
- ( (!(runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE))
- && (!(runloop_st->flags & RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE))
- ))
- return true;
- /* Sanity check 2 - can only remove an override
- * if the specified type is currently active */
- if ( game_specific
- && !(runloop_st->flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE)
- )
- goto error;
- /* Get current options file path */
- current_options_path = path_get(RARCH_PATH_CORE_OPTIONS);
- if (string_is_empty(current_options_path))
- goto error;
- /* Remove current options file, if required */
- if (path_is_valid(current_options_path))
- filestream_delete(current_options_path);
- /* Reload any existing 'parent' options file
- * > If we have removed a game-specific config,
- * check whether a folder-specific config
- * exists */
- if (game_specific &&
- validate_folder_options(
- new_options_path,
- sizeof(new_options_path), false) &&
- path_is_valid(new_options_path))
- folder_options_active = true;
- /* > If a folder-specific config does not exist,
- * or we removed it, check whether we have a
- * top-level config file */
- if (!folder_options_active)
- {
- /* Try core-specific options, if enabled */
- if (per_core_options)
- {
- const char *core_name = runloop_st->system.info.library_name;
- per_core_options = validate_per_core_options(
- new_options_path, sizeof(new_options_path), true,
- core_name, core_name);
- }
- /* ...otherwise use global options */
- if (!per_core_options)
- {
- if (!string_is_empty(path_core_options))
- strlcpy(new_options_path,
- path_core_options, sizeof(new_options_path));
- else if (!path_is_empty(RARCH_PATH_CONFIG))
- fill_pathname_resolve_relative(
- new_options_path, path_get(RARCH_PATH_CONFIG),
- FILE_PATH_CORE_OPTIONS_CONFIG, sizeof(new_options_path));
- }
- }
- if (string_is_empty(new_options_path))
- goto error;
- /* > If we have a valid file, load it */
- if (folder_options_active ||
- path_is_valid(new_options_path))
- {
- size_t i, j;
- if (!(conf = config_file_new_from_path_to_string(new_options_path)))
- goto error;
- for (i = 0; i < coreopts->size; i++)
- {
- struct config_entry_list *entry = NULL;
- struct core_option *option = (struct core_option*)&coreopts->opts[i];
- if (!option)
- continue;
- if (!(entry = config_get_entry(conf, option->key)))
- continue;
- if (string_is_empty(entry->value))
- continue;
- /* Set current config value from file entry */
- for (j = 0; j < option->vals->size; j++)
- {
- if (string_is_equal(option->vals->elems[j].data, entry->value))
- {
- option->index = j;
- break;
- }
- }
- }
- coreopts->updated = true;
- #ifdef HAVE_CHEEVOS
- rcheevos_validate_config_settings();
- #endif
- }
- /* Update runloop status */
- if (folder_options_active)
- {
- path_set(RARCH_PATH_CORE_OPTIONS, new_options_path);
- runloop_st->flags &= ~RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE;
- runloop_st->flags |= RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE;
- }
- else
- {
- path_clear(RARCH_PATH_CORE_OPTIONS);
- runloop_st->flags &= ~(RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE
- | RUNLOOP_FLAG_FOLDER_OPTIONS_ACTIVE);
- /* Update config file path/object stored in
- * core option manager struct */
- strlcpy(coreopts->conf_path, new_options_path,
- sizeof(coreopts->conf_path));
- if (conf)
- {
- config_file_free(coreopts->conf);
- coreopts->conf = conf;
- conf = NULL;
- }
- }
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_OPTIONS_FILE_REMOVED_SUCCESSFULLY),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (conf)
- config_file_free(conf);
- return true;
- error:
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_ERROR_REMOVING_CORE_OPTIONS_FILE),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- if (conf)
- config_file_free(conf);
- return false;
- }
- void core_options_reset(void)
- {
- size_t i;
- runloop_state_t *runloop_st = &runloop_state;
- core_option_manager_t *coreopts = runloop_st->core_options;
- /* If there are no core options, there
- * is nothing to do */
- if (!coreopts || (coreopts->size < 1))
- return;
- for (i = 0; i < coreopts->size; i++)
- coreopts->opts[i].index = coreopts->opts[i].default_index;
- coreopts->updated = true;
- #ifdef HAVE_CHEEVOS
- rcheevos_validate_config_settings();
- #endif
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_CORE_OPTIONS_RESET),
- 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- void core_options_flush(void)
- {
- size_t _len;
- runloop_state_t *runloop_st = &runloop_state;
- core_option_manager_t *coreopts = runloop_st->core_options;
- const char *path_core_options = path_get(RARCH_PATH_CORE_OPTIONS);
- const char *core_options_file = NULL;
- bool success = false;
- char msg[256];
- msg[0] = '\0';
- /* If there are no core options, there
- * is nothing to do */
- if (!coreopts || (coreopts->size < 1))
- return;
- /* Check whether game/folder-specific options file
- * is being used */
- if (!string_is_empty(path_core_options))
- {
- config_file_t *conf_tmp = NULL;
- bool path_valid = path_is_valid(path_core_options);
- /* Attempt to load existing file */
- if (path_valid)
- conf_tmp = config_file_new_from_path_to_string(path_core_options);
- /* Create new file if required */
- if (!conf_tmp)
- conf_tmp = config_file_new_alloc();
- if (conf_tmp)
- {
- core_option_manager_flush(runloop_st->core_options, conf_tmp);
- success = config_file_write(conf_tmp, path_core_options, true);
- config_file_free(conf_tmp);
- }
- }
- else
- {
- /* We are using the 'default' core options file */
- path_core_options = runloop_st->core_options->conf_path;
- if (!string_is_empty(path_core_options))
- {
- core_option_manager_flush(
- runloop_st->core_options,
- runloop_st->core_options->conf);
- /* We must *guarantee* that a file gets written
- * to disk if any options differ from the current
- * options file contents. Must therefore handle
- * the case where the 'default' file does not
- * exist (e.g. if it gets deleted manually while
- * a core is running) */
- if (!path_is_valid(path_core_options))
- runloop_st->core_options->conf->modified = true;
- success = config_file_write(runloop_st->core_options->conf,
- path_core_options, true);
- }
- }
- /* Get options file name for display purposes */
- if (!string_is_empty(path_core_options))
- core_options_file = path_basename_nocompression(path_core_options);
- if (string_is_empty(core_options_file))
- core_options_file = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN);
- if (success)
- {
- /* Log result */
- _len = strlcpy(msg, msg_hash_to_str(MSG_CORE_OPTIONS_FLUSHED),
- sizeof(msg));
- RARCH_LOG(
- "[Core]: Saved core options to \"%s\".\n",
- path_core_options ? path_core_options : "UNKNOWN");
- }
- else
- {
- /* Log result */
- _len = strlcpy(msg, msg_hash_to_str(MSG_CORE_OPTIONS_FLUSH_FAILED),
- sizeof(msg));
- RARCH_LOG(
- "[Core]: Failed to save core options to \"%s\".\n",
- path_core_options ? path_core_options : "UNKNOWN");
- }
- snprintf(msg + _len, sizeof(msg) - _len, " \"%s\"",
- core_options_file);
- runloop_msg_queue_push(
- msg, 1, 100, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- void runloop_msg_queue_push(const char *msg,
- unsigned prio, unsigned duration,
- bool flush,
- char *title,
- enum message_queue_icon icon,
- enum message_queue_category category)
- {
- #if defined(HAVE_GFX_WIDGETS)
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- bool widgets_active = p_dispwidget->active;
- #endif
- #ifdef HAVE_ACCESSIBILITY
- settings_t *settings = config_get_ptr();
- bool accessibility_enable = settings->bools.accessibility_enable;
- unsigned accessibility_narrator_speech_speed = settings->uints.accessibility_narrator_speech_speed;
- access_state_t *access_st = access_state_get_ptr();
- #endif
- runloop_state_t *runloop_st = &runloop_state;
- RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(
- accessibility_enable,
- access_st->enabled))
- accessibility_speak_priority(
- accessibility_enable,
- accessibility_narrator_speech_speed,
- (char*) msg, 0);
- #endif
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- {
- gfx_widgets_msg_queue_push(
- NULL,
- msg,
- roundf((float)duration / 60.0f * 1000.0f),
- title,
- icon,
- category,
- prio,
- flush,
- #ifdef HAVE_MENU
- menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE
- #else
- false
- #endif
- );
- duration = duration * 60 / 1000;
- }
- else
- #endif
- {
- if (flush)
- msg_queue_clear(&runloop_st->msg_queue);
- msg_queue_push(&runloop_st->msg_queue, msg,
- prio, duration,
- title, icon, category);
- runloop_st->msg_queue_size = msg_queue_size(
- &runloop_st->msg_queue);
- }
- ui_companion_driver_msg_queue_push(
- msg, prio, duration, flush);
- RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
- }
- #ifdef HAVE_MENU
- /* Display the libretro core's framebuffer onscreen. */
- static bool display_menu_libretro(
- runloop_state_t *runloop_st,
- input_driver_state_t *input_st,
- float slowmotion_ratio,
- bool libretro_running,
- retro_time_t current_time)
- {
- bool runloop_idle = runloop_st->flags & RUNLOOP_FLAG_IDLE;
- video_driver_state_t*video_st = video_state_get_ptr();
- if ( video_st->poke
- && video_st->poke->set_texture_enable)
- video_st->poke->set_texture_enable(video_st->data, true, false);
- if (libretro_running)
- {
- if (!(input_st->flags & INP_FLAG_BLOCK_LIBRETRO_INPUT))
- input_st->flags |= INP_FLAG_BLOCK_LIBRETRO_INPUT;
- core_run();
- runloop_st->core_runtime_usec +=
- runloop_core_runtime_tick(runloop_st, slowmotion_ratio, current_time);
- input_st->flags &= ~INP_FLAG_BLOCK_LIBRETRO_INPUT;
- return false;
- }
- if (runloop_idle)
- {
- #ifdef HAVE_PRESENCE
- presence_userdata_t userdata;
- userdata.status = PRESENCE_GAME_PAUSED;
- command_event(CMD_EVENT_PRESENCE_UPDATE, &userdata);
- #endif
- return false;
- }
- return true;
- }
- #endif
- #define HOTKEY_CHECK(cmd1, cmd2, cond, cond2) \
- { \
- static bool old_pressed = false; \
- bool pressed = BIT256_GET(current_bits, cmd1); \
- if (pressed && !old_pressed) \
- if (cond) \
- command_event(cmd2, cond2); \
- old_pressed = pressed; \
- }
- #define HOTKEY_CHECK3(cmd1, cmd2, cmd3, cmd4, cmd5, cmd6) \
- { \
- static bool old_pressed = false; \
- static bool old_pressed2 = false; \
- static bool old_pressed3 = false; \
- bool pressed = BIT256_GET(current_bits, cmd1); \
- bool pressed2 = BIT256_GET(current_bits, cmd3); \
- bool pressed3 = BIT256_GET(current_bits, cmd5); \
- if (pressed && !old_pressed) \
- command_event(cmd2, (void*)(intptr_t)0); \
- else if (pressed2 && !old_pressed2) \
- command_event(cmd4, (void*)(intptr_t)0); \
- else if (pressed3 && !old_pressed3) \
- command_event(cmd6, (void*)(intptr_t)0); \
- old_pressed = pressed; \
- old_pressed2 = pressed2; \
- old_pressed3 = pressed3; \
- }
- static void runloop_pause_toggle(
- bool *runloop_paused_hotkey,
- bool pause_pressed, bool old_pause_pressed,
- bool focused, bool old_focus)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (focused)
- {
- if (pause_pressed && !old_pause_pressed)
- {
- /* Keep track of hotkey triggered pause to
- * distinguish it from menu triggered pause */
- *runloop_paused_hotkey = !(runloop_st->flags & RUNLOOP_FLAG_PAUSED);
- command_event(CMD_EVENT_PAUSE_TOGGLE, NULL);
- }
- else if (!old_focus)
- command_event(CMD_EVENT_UNPAUSE, NULL);
- }
- else if (old_focus)
- command_event(CMD_EVENT_PAUSE, NULL);
- }
- static enum runloop_state_enum runloop_check_state(
- bool error_on_init,
- settings_t *settings,
- retro_time_t current_time)
- {
- input_bits_t current_bits;
- #ifdef HAVE_MENU
- static input_bits_t last_input = {{0}};
- #endif
- uico_driver_state_t *uico_st = uico_state_get_ptr();
- input_driver_state_t *input_st = input_state_get_ptr();
- video_driver_state_t *video_st = video_state_get_ptr();
- gfx_display_t *p_disp = disp_get_ptr();
- runloop_state_t *runloop_st = &runloop_state;
- static bool old_focus = true;
- static bool runloop_paused_hotkey = false;
- struct retro_callbacks *cbs = &runloop_st->retro_ctx;
- bool is_focused = false;
- bool is_alive = false;
- uint64_t frame_count = 0;
- bool focused = true;
- bool rarch_is_initialized = runloop_st->flags & RUNLOOP_FLAG_IS_INITED;
- bool runloop_paused = runloop_st->flags & RUNLOOP_FLAG_PAUSED;
- bool pause_nonactive = settings->bools.pause_nonactive;
- unsigned quit_gamepad_combo = settings->uints.input_quit_gamepad_combo;
- #ifdef HAVE_MENU
- struct menu_state *menu_st = menu_state_get_ptr();
- menu_handle_t *menu = menu_st->driver_data;
- unsigned menu_toggle_gamepad_combo = settings->uints.input_menu_toggle_gamepad_combo;
- bool menu_driver_binding_state = menu_st->flags & MENU_ST_FLAG_IS_BINDING;
- bool menu_is_alive = menu_st->flags & MENU_ST_FLAG_ALIVE;
- bool display_kb = menu_input_dialog_get_display_kb();
- #endif
- #if defined(HAVE_GFX_WIDGETS)
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- bool widgets_active = p_dispwidget->active;
- #endif
- #ifdef HAVE_CHEEVOS
- bool cheevos_hardcore_active = false;
- #endif
- #ifdef ANDROID
- struct android_app *app = (struct android_app*)g_android;
- #endif
- #if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS)
- if (p_dispwidget->ai_service_overlay_state == 3)
- {
- command_event(CMD_EVENT_PAUSE, NULL);
- p_dispwidget->ai_service_overlay_state = 1;
- }
- #endif
- #ifdef HAVE_LIBNX
- /* Should be called once per frame */
- if (!appletMainLoop())
- return RUNLOOP_STATE_QUIT;
- #endif
- #ifdef _3DS
- /* Should be called once per frame */
- if (!aptMainLoop())
- return RUNLOOP_STATE_QUIT;
- #endif
- BIT256_CLEAR_ALL_PTR(¤t_bits);
- input_st->flags &= ~(INP_FLAG_BLOCK_LIBRETRO_INPUT
- | INP_FLAG_BLOCK_HOTKEY);
- if (input_st->flags & INP_FLAG_KB_MAPPING_BLOCKED)
- input_st->flags |= INP_FLAG_BLOCK_HOTKEY;
- input_driver_collect_system_input(input_st, settings, ¤t_bits);
- #ifdef HAVE_MENU
- last_input = current_bits;
- if (
- ((menu_toggle_gamepad_combo != INPUT_COMBO_NONE) &&
- input_driver_button_combo(
- menu_toggle_gamepad_combo,
- current_time,
- &last_input)))
- BIT256_SET(current_bits, RARCH_MENU_TOGGLE);
- if (menu_st->input_driver_flushing_input > 0)
- {
- bool input_active = bits_any_set(current_bits.data, ARRAY_SIZE(current_bits.data));
- /* Don't count 'enable_hotkey' as active input */
- if ( input_active
- && BIT256_GET(current_bits, RARCH_ENABLE_HOTKEY)
- && !BIT256_GET(current_bits, RARCH_MENU_TOGGLE))
- input_active = false;
- if (!input_active)
- menu_st->input_driver_flushing_input--;
- if (input_active || (menu_st->input_driver_flushing_input > 0))
- {
- BIT256_CLEAR_ALL(current_bits);
- if (runloop_paused && !runloop_paused_hotkey && settings->bools.menu_pause_libretro)
- BIT256_SET(current_bits, RARCH_PAUSE_TOGGLE);
- else if (runloop_paused_hotkey)
- {
- /* Restore pause if pause is triggered with both hotkey and menu,
- * and restore cached video frame to continue properly to
- * paused state from non-paused menu */
- if (settings->bools.menu_pause_libretro)
- command_event(CMD_EVENT_PAUSE, NULL);
- else
- video_driver_cached_frame();
- }
- }
- }
- #endif
- if (!VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st))
- {
- const ui_application_t *application = uico_st->drv
- ? uico_st->drv->application
- : NULL;
- if (application)
- application->process_events();
- }
- frame_count = video_st->frame_count;
- is_alive = video_st->current_video
- ? video_st->current_video->alive(video_st->data)
- : true;
- is_focused = VIDEO_HAS_FOCUS(video_st);
- #ifdef HAVE_MENU
- if (menu_driver_binding_state)
- BIT256_CLEAR_ALL(current_bits);
- #endif
- /* Check fullscreen hotkey */
- HOTKEY_CHECK(RARCH_FULLSCREEN_TOGGLE_KEY, CMD_EVENT_FULLSCREEN_TOGGLE, true, NULL);
- /* Check mouse grab hotkey */
- HOTKEY_CHECK(RARCH_GRAB_MOUSE_TOGGLE, CMD_EVENT_GRAB_MOUSE_TOGGLE, true, NULL);
- /* Automatic mouse grab on focus */
- if ( settings->bools.input_auto_mouse_grab
- && (is_focused)
- && (is_focused != (((runloop_st->flags & RUNLOOP_FLAG_FOCUSED)) > 0))
- && !(input_st->flags & INP_FLAG_GRAB_MOUSE_STATE))
- command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL);
- if (is_focused)
- runloop_st->flags |= RUNLOOP_FLAG_FOCUSED;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_FOCUSED;
- #ifdef HAVE_OVERLAY
- if (settings->bools.input_overlay_enable)
- {
- static char prev_overlay_restore = false;
- static unsigned last_width = 0;
- static unsigned last_height = 0;
- unsigned video_driver_width = video_st->width;
- unsigned video_driver_height = video_st->height;
- bool check_next_rotation = true;
- bool input_overlay_hide_when_gamepad_connected = settings->bools.input_overlay_hide_when_gamepad_connected;
- bool input_overlay_auto_rotate = settings->bools.input_overlay_auto_rotate;
- /* Check whether overlay should be hidden
- * when a gamepad is connected */
- if (input_overlay_hide_when_gamepad_connected)
- {
- static bool last_controller_connected = false;
- bool controller_connected = (input_config_get_device_name(0) != NULL);
- if (controller_connected != last_controller_connected)
- {
- if (controller_connected)
- input_overlay_deinit();
- else
- input_overlay_init();
- last_controller_connected = controller_connected;
- }
- }
- /* Check next overlay hotkey */
- HOTKEY_CHECK(RARCH_OVERLAY_NEXT, CMD_EVENT_OVERLAY_NEXT, true, &check_next_rotation);
- /* Ensure overlay is restored after displaying OSK */
- if (input_st->flags & INP_FLAG_KB_LINEFEED_ENABLE)
- prev_overlay_restore = true;
- else if (prev_overlay_restore)
- {
- input_overlay_init();
- prev_overlay_restore = false;
- }
- /* Check whether video aspect has changed */
- if ((video_driver_width != last_width) ||
- (video_driver_height != last_height))
- {
- /* Update scaling/offset factors */
- command_event(CMD_EVENT_OVERLAY_SET_SCALE_FACTOR, NULL);
- /* Check overlay rotation, if required */
- if (input_overlay_auto_rotate)
- input_overlay_auto_rotate_(
- video_st->width,
- video_st->height,
- settings->bools.input_overlay_enable,
- input_st->overlay_ptr);
- last_width = video_driver_width;
- last_height = video_driver_height;
- }
- /* Check OSK hotkey */
- HOTKEY_CHECK(RARCH_OSK, CMD_EVENT_OSK_TOGGLE, true, NULL);
- }
- #endif
- /*
- * If the Aspect Ratio is FULL then update the aspect ratio to the
- * current video driver aspect ratio (The full window)
- *
- * TODO/FIXME
- * Should possibly be refactored to have last width & driver width & height
- * only be done once when we are using an overlay OR using aspect ratio
- * full
- */
- if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_FULL)
- {
- static unsigned last_width = 0;
- static unsigned last_height = 0;
- unsigned video_driver_width = video_st->width;
- unsigned video_driver_height = video_st->height;
- /* Check whether video aspect has changed */
- if ((video_driver_width != last_width) ||
- (video_driver_height != last_height))
- {
- /* Update set aspect ratio so the full matches the current video width & height */
- command_event(CMD_EVENT_VIDEO_SET_ASPECT_RATIO, NULL);
- last_width = video_driver_width;
- last_height = video_driver_height;
- }
- }
- #ifdef ANDROID
- /* transfer select + start button */
- if (BIT256_GET(current_bits, RARCH_JOYPAD_SELECT)
- && BIT256_GET(current_bits, RARCH_JOYPAD_START)
- && app->openGameDialogRequested == 0) {
- android_app_write_cmd(app, APP_CMD_GAME_DIALOG_OPENED);
- }
- #endif
- /* Check quit hotkey */
- {
- bool trig_quit_key, quit_press_twice;
- static bool quit_key = false;
- static bool old_quit_key = false;
- static bool runloop_exec = false;
- quit_key = BIT256_GET(
- current_bits, RARCH_QUIT_KEY);
- trig_quit_key = quit_key && !old_quit_key;
- /* Check for quit gamepad combo */
- if (!trig_quit_key &&
- ((quit_gamepad_combo != INPUT_COMBO_NONE) &&
- input_driver_button_combo(
- quit_gamepad_combo,
- current_time,
- ¤t_bits)))
- trig_quit_key = true;
- old_quit_key = quit_key;
- quit_press_twice = settings->bools.quit_press_twice;
- /* Check double press if enabled */
- if (trig_quit_key && quit_press_twice)
- {
- static retro_time_t quit_key_time = 0;
- retro_time_t cur_time = current_time;
- trig_quit_key = (cur_time - quit_key_time < QUIT_DELAY_USEC);
- quit_key_time = cur_time;
- if (!trig_quit_key)
- {
- float target_hz = 0.0;
- runloop_environment_cb(
- RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE, &target_hz);
- runloop_msg_queue_push(msg_hash_to_str(MSG_PRESS_AGAIN_TO_QUIT), 1,
- QUIT_DELAY_USEC * target_hz / 1000000,
- true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- if (RUNLOOP_TIME_TO_EXIT(trig_quit_key))
- {
- bool quit_runloop = false;
- #ifdef HAVE_SCREENSHOTS
- unsigned runloop_max_frames = runloop_st->max_frames;
- if ((runloop_max_frames != 0)
- && (frame_count >= runloop_max_frames)
- && (runloop_st->flags & RUNLOOP_FLAG_MAX_FRAMES_SCREENSHOT))
- {
- const char *screenshot_path = NULL;
- bool fullpath = false;
- if (string_is_empty(runloop_st->max_frames_screenshot_path))
- screenshot_path = path_get(RARCH_PATH_BASENAME);
- else
- {
- fullpath = true;
- screenshot_path = runloop_st->max_frames_screenshot_path;
- }
- RARCH_LOG("Taking a screenshot before exiting...\n");
- /* Take a screenshot before we exit. */
- if (!take_screenshot(settings->paths.directory_screenshot,
- screenshot_path, false,
- video_driver_cached_frame_has_valid_framebuffer(), fullpath, false))
- {
- RARCH_ERR("Could not take a screenshot before exiting.\n");
- }
- }
- #endif
- if (runloop_exec)
- runloop_exec = false;
- if (runloop_st->flags & RUNLOOP_FLAG_CORE_SHUTDOWN_INITIATED)
- {
- bool load_dummy_core = false;
- runloop_st->flags &= ~RUNLOOP_FLAG_CORE_SHUTDOWN_INITIATED;
- /* Check whether dummy core should be loaded
- * instead of exiting RetroArch completely
- * (aborts shutdown if invoked) */
- if (settings->bools.load_dummy_on_core_shutdown)
- {
- load_dummy_core = true;
- runloop_st->flags &= ~RUNLOOP_FLAG_SHUTDOWN_INITIATED;
- }
- /* Unload current core, and load dummy if
- * required */
- if (!command_event(CMD_EVENT_UNLOAD_CORE, &load_dummy_core))
- {
- runloop_st->flags |= RUNLOOP_FLAG_SHUTDOWN_INITIATED;
- quit_runloop = true;
- }
- if (!load_dummy_core)
- quit_runloop = true;
- }
- else
- quit_runloop = true;
- runloop_st->flags &= ~RUNLOOP_FLAG_CORE_RUNNING;
- if (quit_runloop)
- {
- old_quit_key = quit_key;
- return RUNLOOP_STATE_QUIT;
- }
- }
- }
- #if defined(HAVE_MENU) || defined(HAVE_GFX_WIDGETS)
- gfx_animation_update(
- current_time,
- settings->bools.menu_timedate_enable,
- settings->floats.menu_ticker_speed,
- video_st->width,
- video_st->height);
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- {
- bool rarch_force_fullscreen = video_st->flags &
- VIDEO_FLAG_FORCE_FULLSCREEN;
- bool video_is_fullscreen = settings->bools.video_fullscreen ||
- rarch_force_fullscreen;
- RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
- gfx_widgets_iterate(
- p_disp,
- settings,
- video_st->width,
- video_st->height,
- video_is_fullscreen,
- settings->paths.directory_assets,
- settings->paths.path_font,
- VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st));
- RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
- }
- #endif
- #ifdef HAVE_MENU
- if (menu_is_alive)
- {
- enum menu_action action;
- static input_bits_t old_input = {{0}};
- static enum menu_action
- old_action = MENU_ACTION_CANCEL;
- struct menu_state *menu_st = menu_state_get_ptr();
- bool focused = false;
- input_bits_t trigger_input = current_bits;
- unsigned screensaver_timeout = settings->uints.menu_screensaver_timeout;
- /* Get current time */
- menu_st->current_time_us = current_time;
- cbs->poll_cb();
- bits_clear_bits(trigger_input.data, old_input.data,
- ARRAY_SIZE(trigger_input.data));
- action = (enum menu_action)menu_event(
- settings,
- ¤t_bits, &trigger_input, display_kb);
- #ifdef HAVE_NETWORKING
- if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_PAUSE, NULL))
- focused = true;
- else
- #endif
- {
- if (pause_nonactive)
- focused = is_focused && (!(uico_st->flags & UICO_ST_FLAG_IS_ON_FOREGROUND));
- else
- focused = (!(uico_st->flags & UICO_ST_FLAG_IS_ON_FOREGROUND));
- }
- if (action == old_action)
- {
- retro_time_t press_time = current_time;
- if (action == MENU_ACTION_NOOP)
- menu_st->noop_press_time = press_time - menu_st->noop_start_time;
- else
- menu_st->action_press_time = press_time - menu_st->action_start_time;
- }
- else
- {
- if (action == MENU_ACTION_NOOP)
- {
- menu_st->noop_start_time = current_time;
- menu_st->noop_press_time = 0;
- if (menu_st->prev_action == old_action)
- menu_st->action_start_time = menu_st->prev_start_time;
- else
- menu_st->action_start_time = current_time;
- }
- else
- {
- if ( menu_st->prev_action == action
- && menu_st->noop_press_time < 200000) /* 250ms */
- {
- menu_st->action_start_time = menu_st->prev_start_time;
- menu_st->action_press_time = current_time - menu_st->action_start_time;
- }
- else
- {
- menu_st->prev_start_time = current_time;
- menu_st->prev_action = action;
- menu_st->action_press_time = 0;
- }
- }
- }
- /* Check whether menu screensaver should be enabled */
- if ( (screensaver_timeout > 0)
- && (menu_st->flags & MENU_ST_FLAG_SCREENSAVER_SUPPORTED)
- && (!(menu_st->flags & MENU_ST_FLAG_SCREENSAVER_ACTIVE))
- && ((menu_st->current_time_us - menu_st->input_last_time_us)
- > ((retro_time_t)screensaver_timeout * 1000000)))
- {
- menu_st->flags |= MENU_ST_FLAG_SCREENSAVER_ACTIVE;
- if (menu_st->driver_ctx->environ_cb)
- menu_st->driver_ctx->environ_cb(MENU_ENVIRON_ENABLE_SCREENSAVER,
- NULL, menu_st->userdata);
- }
- /* Iterate the menu driver for one frame. */
- /* If the user had requested that the Quick Menu
- * be spawned during the previous frame, do this now
- * and exit the function to go to the next frame. */
- if (menu_st->flags & MENU_ST_FLAG_PENDING_QUICK_MENU)
- {
- /* We are going to push a new menu; ensure
- * that the current one is cached for animation
- * purposes */
- if (menu_st->driver_ctx && menu_st->driver_ctx->list_cache)
- menu_st->driver_ctx->list_cache(menu_st->userdata,
- MENU_LIST_PLAIN, MENU_ACTION_NOOP);
- p_disp->flags |= GFX_DISP_FLAG_MSG_FORCE;
- generic_action_ok_displaylist_push("", NULL,
- "", 0, 0, 0, ACTION_OK_DL_CONTENT_SETTINGS);
- menu_st->selection_ptr = 0;
- menu_st->flags &= ~MENU_ST_FLAG_PENDING_QUICK_MENU;
- }
- else if (!menu_driver_iterate(
- menu_st,
- p_disp,
- anim_get_ptr(),
- settings,
- action, current_time))
- {
- if (error_on_init)
- {
- content_ctx_info_t content_info = {0};
- task_push_start_dummy_core(&content_info);
- }
- else
- retroarch_menu_running_finished(false);
- }
- if (focused || !(runloop_st->flags & RUNLOOP_FLAG_IDLE))
- {
- bool runloop_is_inited = runloop_st->flags & RUNLOOP_FLAG_IS_INITED;
- #ifdef HAVE_NETWORKING
- bool menu_pause_libretro = settings->bools.menu_pause_libretro &&
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_PAUSE, NULL);
- #else
- bool menu_pause_libretro = settings->bools.menu_pause_libretro;
- #endif
- bool libretro_running = !(runloop_st->flags & RUNLOOP_FLAG_PAUSED)
- && !menu_pause_libretro
- && runloop_is_inited
- && (runloop_st->current_core_type != CORE_TYPE_DUMMY);
- if (menu)
- {
- if (BIT64_GET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER)
- != BIT64_GET(menu->state, MENU_STATE_RENDER_MESSAGEBOX))
- BIT64_SET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER);
- if (BIT64_GET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER))
- p_disp->flags |= GFX_DISP_FLAG_FB_DIRTY;
- if (BIT64_GET(menu->state, MENU_STATE_RENDER_MESSAGEBOX)
- && !string_is_empty(menu->menu_state_msg))
- {
- if (menu->driver_ctx->render_messagebox)
- menu->driver_ctx->render_messagebox(
- menu->userdata,
- menu->menu_state_msg);
- if (uico_st->flags & UICO_ST_FLAG_IS_ON_FOREGROUND)
- {
- if ( uico_st->drv
- && uico_st->drv->render_messagebox)
- uico_st->drv->render_messagebox(menu->menu_state_msg);
- }
- }
- if (BIT64_GET(menu->state, MENU_STATE_BLIT))
- {
- if (menu->driver_ctx->render)
- menu->driver_ctx->render(
- menu->userdata,
- video_st->width,
- video_st->height,
- runloop_st->flags & RUNLOOP_FLAG_IDLE);
- }
- if ( (menu_st->flags & MENU_ST_FLAG_ALIVE)
- && !(runloop_st->flags & RUNLOOP_FLAG_IDLE))
- if (display_menu_libretro(runloop_st, input_st,
- settings->floats.slowmotion_ratio,
- libretro_running, current_time))
- video_driver_cached_frame();
- if (menu->driver_ctx->set_texture)
- menu->driver_ctx->set_texture(menu->userdata);
- menu->state = 0;
- }
- if (settings->bools.audio_enable_menu &&
- !libretro_running)
- audio_driver_menu_sample();
- }
- old_input = current_bits;
- old_action = action;
- if (!focused || (runloop_st->flags & RUNLOOP_FLAG_IDLE))
- return RUNLOOP_STATE_POLLED_AND_SLEEP;
- }
- else
- #endif
- #endif
- {
- if (runloop_st->flags & RUNLOOP_FLAG_IDLE)
- {
- cbs->poll_cb();
- return RUNLOOP_STATE_POLLED_AND_SLEEP;
- }
- }
- /* Check Game Focus hotkey */
- {
- enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_TOGGLE;
- HOTKEY_CHECK(RARCH_GAME_FOCUS_TOGGLE, CMD_EVENT_GAME_FOCUS_TOGGLE, true, &game_focus_cmd);
- }
- /* Check UI companion hotkey */
- HOTKEY_CHECK(RARCH_UI_COMPANION_TOGGLE, CMD_EVENT_UI_COMPANION_TOGGLE, true, NULL);
- /* Check close content hotkey */
- HOTKEY_CHECK(RARCH_CLOSE_CONTENT_KEY, CMD_EVENT_CLOSE_CONTENT, true, NULL);
- #ifdef HAVE_MENU
- /* Check menu hotkey */
- {
- static bool old_pressed = false;
- char *menu_driver = settings->arrays.menu_driver;
- bool pressed = BIT256_GET(current_bits, RARCH_MENU_TOGGLE)
- && !string_is_equal(menu_driver, "null");
- bool core_type_is_dummy = runloop_st->current_core_type == CORE_TYPE_DUMMY;
- if ( (pressed && !old_pressed) ||
- core_type_is_dummy)
- {
- if (menu_st->flags & MENU_ST_FLAG_ALIVE)
- {
- if (rarch_is_initialized && !core_type_is_dummy)
- retroarch_menu_running_finished(false);
- }
- else
- retroarch_menu_running();
- }
- old_pressed = pressed;
- }
- #endif
- /* Check FPS hotkey */
- HOTKEY_CHECK(RARCH_FPS_TOGGLE, CMD_EVENT_FPS_TOGGLE, true, NULL);
- /* Check statistics hotkey */
- HOTKEY_CHECK(RARCH_STATISTICS_TOGGLE, CMD_EVENT_STATISTICS_TOGGLE, true, NULL);
- /* Check netplay host hotkey */
- HOTKEY_CHECK(RARCH_NETPLAY_HOST_TOGGLE, CMD_EVENT_NETPLAY_HOST_TOGGLE, true, NULL);
- /* Volume stepping + acceleration */
- {
- static unsigned volume_hotkey_delay = 0;
- static unsigned volume_hotkey_delay_active = 0;
- unsigned volume_hotkey_delay_default = 6;
- bool volume_hotkey_up = BIT256_GET(
- current_bits, RARCH_VOLUME_UP);
- bool volume_hotkey_down = BIT256_GET(
- current_bits, RARCH_VOLUME_DOWN);
- if ( (volume_hotkey_up && !volume_hotkey_down) ||
- (volume_hotkey_down && !volume_hotkey_up))
- {
- if (volume_hotkey_delay > 0)
- volume_hotkey_delay--;
- else
- {
- if (volume_hotkey_up)
- command_event(CMD_EVENT_VOLUME_UP, NULL);
- else if (volume_hotkey_down)
- command_event(CMD_EVENT_VOLUME_DOWN, NULL);
- if (volume_hotkey_delay_active > 0)
- volume_hotkey_delay_active--;
- volume_hotkey_delay = volume_hotkey_delay_active;
- }
- }
- else
- {
- volume_hotkey_delay = 0;
- volume_hotkey_delay_active = volume_hotkey_delay_default;
- }
- }
- /* Check audio mute hotkey */
- HOTKEY_CHECK(RARCH_MUTE, CMD_EVENT_AUDIO_MUTE_TOGGLE, true, NULL);
- #ifdef HAVE_SCREENSHOTS
- /* Check screenshot hotkey */
- HOTKEY_CHECK(RARCH_SCREENSHOT, CMD_EVENT_TAKE_SCREENSHOT, true, NULL);
- #endif
- #ifdef HAVE_CHEEVOS
- if (!cheevos_hardcore_active)
- #endif
- {
- /* Check rewind hotkey */
- /* > Must do this before MENU_ITERATE to not lose rewind steps
- * while menu is active when menu pause is disabled */
- {
- #ifdef HAVE_REWIND
- char s[128];
- bool rewinding = false;
- static bool old_rewind_pressed = false;
- bool rewind_pressed = BIT256_GET(current_bits, RARCH_REWIND);
- unsigned t = 0;
- s[0] = '\0';
- #ifdef HAVE_MENU
- /* Don't allow rewinding while menu is active */
- if (menu_st->flags & MENU_ST_FLAG_ALIVE)
- rewind_pressed = false;
- #endif
- /* Prevent rewind hold while paused to rewind only one frame */
- if ( runloop_paused
- && rewind_pressed
- && old_rewind_pressed
- && !runloop_st->run_frames_and_pause)
- {
- cbs->poll_cb();
- return RUNLOOP_STATE_PAUSE;
- }
- rewinding = state_manager_check_rewind(
- &runloop_st->rewind_st,
- &runloop_st->current_core,
- rewind_pressed,
- settings->uints.rewind_granularity,
- runloop_paused
- #ifdef HAVE_MENU
- || ( (menu_st->flags & MENU_ST_FLAG_ALIVE)
- && settings->bools.menu_pause_libretro)
- #endif
- ,
- s, sizeof(s), &t);
- old_rewind_pressed = rewind_pressed;
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- {
- if (rewinding)
- video_st->flags |= VIDEO_FLAG_WIDGETS_REWINDING;
- else
- video_st->flags &= ~VIDEO_FLAG_WIDGETS_REWINDING;
- }
- else
- #endif
- {
- if (rewinding)
- runloop_msg_queue_push(s, 0, t, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- if (rewinding && runloop_paused
- #ifdef HAVE_MENU
- && !(menu_st->flags & MENU_ST_FLAG_ALIVE)
- #endif
- )
- {
- cbs->poll_cb();
- /* Run a few frames on first press after pausing to
- * prevent going forwards for the first frame */
- if (runloop_st->run_frames_and_pause == -1)
- {
- runloop_st->flags &= ~RUNLOOP_FLAG_PAUSED;
- runloop_st->run_frames_and_pause = 3;
- }
- return RUNLOOP_STATE_ITERATE;
- }
- #endif
- }
- }
- /* Check pause hotkey in menu */
- #ifdef HAVE_MENU
- if (menu_st->flags & MENU_ST_FLAG_ALIVE)
- {
- static bool old_pause_pressed = false;
- bool pause_pressed = BIT256_GET(current_bits, RARCH_PAUSE_TOGGLE);
- /* Decide pause hotkey */
- runloop_pause_toggle(&runloop_paused_hotkey,
- pause_pressed, old_pause_pressed,
- focused, old_focus);
- old_focus = focused;
- old_pause_pressed = pause_pressed;
- }
- #endif
- #ifdef HAVE_MENU
- /* Stop checking the rest of the hotkeys if menu is alive */
- if (menu_st->flags & MENU_ST_FLAG_ALIVE)
- {
- float fastforward_ratio = runloop_get_fastforward_ratio(settings,
- &runloop_st->fastmotion_override.current);
- if (!settings->bools.menu_throttle_framerate && !fastforward_ratio)
- return RUNLOOP_STATE_MENU_ITERATE;
- return RUNLOOP_STATE_END;
- }
- #endif
- #ifdef HAVE_NETWORKING
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_PAUSE, NULL))
- #endif
- if (pause_nonactive)
- focused = is_focused;
- /* Check pause hotkey */
- {
- static bool old_frameadvance = false;
- static bool old_pause_pressed = false;
- static bool pauseframeadvance = false;
- bool frameadvance_pressed = false;
- bool frameadvance_trigger = false;
- bool pause_pressed = BIT256_GET(current_bits, RARCH_PAUSE_TOGGLE);
- /* Reset frameadvance pause when triggering pause */
- if (pause_pressed)
- pauseframeadvance = false;
- /* Allow unpausing with Start */
- if (runloop_paused && settings->bools.pause_on_disconnect)
- pause_pressed |= BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_START);
- #ifdef HAVE_CHEEVOS
- /* Make sure not to evaluate this before calling menu_driver_iterate
- * as that may change its value */
- cheevos_hardcore_active = rcheevos_hardcore_active();
- if (cheevos_hardcore_active)
- {
- static int unpaused_frames = 0;
- if (runloop_st->flags & RUNLOOP_FLAG_PAUSED)
- unpaused_frames = 0;
- else
- /* Frame advance is not allowed when achievement hardcore is active */
- {
- /* Limit pause to approximately three times per second (depending on core framerate) */
- if (unpaused_frames < 20)
- {
- ++unpaused_frames;
- pause_pressed = false;
- }
- }
- }
- else
- #endif
- {
- frameadvance_pressed = BIT256_GET(current_bits, RARCH_FRAMEADVANCE);
- frameadvance_trigger = frameadvance_pressed && !old_frameadvance;
- /* FRAMEADVANCE will set us into special pause mode. */
- if (frameadvance_trigger)
- {
- pauseframeadvance = true;
- if (!(runloop_st->flags & RUNLOOP_FLAG_PAUSED))
- pause_pressed = true;
- }
- }
- /* Decide pause hotkey */
- runloop_pause_toggle(&runloop_paused_hotkey,
- pause_pressed, old_pause_pressed,
- focused, old_focus);
- old_focus = focused;
- old_pause_pressed = pause_pressed;
- old_frameadvance = frameadvance_pressed;
- if (runloop_st->flags & RUNLOOP_FLAG_PAUSED)
- {
- #ifdef HAVE_REWIND
- /* Frame advance must also trigger rewind save */
- if (frameadvance_trigger && runloop_paused)
- state_manager_check_rewind(
- &runloop_st->rewind_st,
- &runloop_st->current_core,
- false,
- settings->uints.rewind_granularity,
- false,
- NULL, 0, NULL);
- #endif
- /* Check if it's not oneshot */
- #ifdef HAVE_REWIND
- if (!(frameadvance_trigger || BIT256_GET(current_bits, RARCH_REWIND)))
- #else
- if (!frameadvance_trigger)
- #endif
- focused = false;
- else
- runloop_paused = false;
- /* Drop to RUNLOOP_STATE_POLLED_AND_SLEEP if frameadvance is triggered */
- if (pauseframeadvance)
- runloop_paused = false;
- }
- }
- /* Check recording hotkey */
- HOTKEY_CHECK(RARCH_RECORDING_TOGGLE, CMD_EVENT_RECORDING_TOGGLE, true, NULL);
- /* Check streaming hotkey */
- HOTKEY_CHECK(RARCH_STREAMING_TOGGLE, CMD_EVENT_STREAMING_TOGGLE, true, NULL);
- /* Check Run-Ahead hotkey */
- HOTKEY_CHECK(RARCH_RUNAHEAD_TOGGLE, CMD_EVENT_RUNAHEAD_TOGGLE, true, NULL);
- /* Check Preemptive Frames hotkey */
- HOTKEY_CHECK(RARCH_PREEMPT_TOGGLE, CMD_EVENT_PREEMPT_TOGGLE, true, NULL);
- /* Check AI Service hotkey */
- HOTKEY_CHECK(RARCH_AI_SERVICE, CMD_EVENT_AI_SERVICE_TOGGLE, true, NULL);
- #ifdef HAVE_NETWORKING
- /* Check netplay hotkeys */
- HOTKEY_CHECK(RARCH_NETPLAY_PING_TOGGLE, CMD_EVENT_NETPLAY_PING_TOGGLE, true, NULL);
- HOTKEY_CHECK(RARCH_NETPLAY_GAME_WATCH, CMD_EVENT_NETPLAY_GAME_WATCH, true, NULL);
- HOTKEY_CHECK(RARCH_NETPLAY_PLAYER_CHAT, CMD_EVENT_NETPLAY_PLAYER_CHAT, true, NULL);
- HOTKEY_CHECK(RARCH_NETPLAY_FADE_CHAT_TOGGLE, CMD_EVENT_NETPLAY_FADE_CHAT_TOGGLE, true, NULL);
- #endif
- #ifdef HAVE_ACCESSIBILITY
- #ifdef HAVE_TRANSLATE
- /* Copy over the retropad state to a buffer for the translate service
- to send off if it's run. */
- if (settings->bools.ai_service_enable)
- {
- input_st->ai_gamepad_state[0] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_B);
- input_st->ai_gamepad_state[1] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_Y);
- input_st->ai_gamepad_state[2] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_SELECT);
- input_st->ai_gamepad_state[3] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_START);
- input_st->ai_gamepad_state[4] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_UP);
- input_st->ai_gamepad_state[5] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_DOWN);
- input_st->ai_gamepad_state[6] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_LEFT);
- input_st->ai_gamepad_state[7] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_RIGHT);
- input_st->ai_gamepad_state[8] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_A);
- input_st->ai_gamepad_state[9] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_X);
- input_st->ai_gamepad_state[10] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_L);
- input_st->ai_gamepad_state[11] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_R);
- input_st->ai_gamepad_state[12] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_L2);
- input_st->ai_gamepad_state[13] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_R2);
- input_st->ai_gamepad_state[14] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_L3);
- input_st->ai_gamepad_state[15] = BIT256_GET(current_bits, RETRO_DEVICE_ID_JOYPAD_R3);
- }
- #endif
- #endif
- if (!focused && !runloop_paused)
- {
- cbs->poll_cb();
- return RUNLOOP_STATE_POLLED_AND_SLEEP;
- }
- /* Apply any pending fastmotion override parameters */
- if (runloop_st->fastmotion_override.pending)
- {
- runloop_apply_fastmotion_override(runloop_st, settings);
- runloop_st->fastmotion_override.pending = false;
- }
- /* Check fastmotion hotkeys */
- /* To avoid continuous switching if we hold the button down, we require
- * that the button must go from pressed to unpressed back to pressed
- * to be able to toggle between them.
- */
- if (!runloop_st->fastmotion_override.current.inhibit_toggle)
- {
- static bool old_button_state = false;
- static bool old_hold_button_state = false;
- bool new_button_state = BIT256_GET(
- current_bits, RARCH_FAST_FORWARD_KEY);
- bool new_hold_button_state = BIT256_GET(
- current_bits, RARCH_FAST_FORWARD_HOLD_KEY);
- bool check2 = new_button_state
- && !old_button_state;
- if (!check2)
- check2 = old_hold_button_state != new_hold_button_state;
- /* Don't allow fastmotion while paused */
- if (runloop_paused)
- {
- check2 = true;
- new_button_state = false;
- new_hold_button_state = false;
- input_st->flags |= INP_FLAG_NONBLOCKING;
- }
- if (check2)
- {
- if (input_st->flags & INP_FLAG_NONBLOCKING)
- {
- input_st->flags &= ~INP_FLAG_NONBLOCKING;
- runloop_st->flags &= ~RUNLOOP_FLAG_FASTMOTION;
- runloop_st->fastforward_after_frames = 1;
- }
- else
- {
- input_st->flags |= INP_FLAG_NONBLOCKING;
- runloop_st->flags |= RUNLOOP_FLAG_FASTMOTION;
- command_event(CMD_EVENT_SET_FRAME_LIMIT, NULL);
- }
- driver_set_nonblock_state();
- /* Reset frame time counter when toggling
- * fast-forward off, if required */
- if ( !(runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
- && settings->bools.frame_time_counter_reset_after_fastforwarding)
- video_st->frame_time_count = 0;
- }
- old_button_state = new_button_state;
- old_hold_button_state = new_hold_button_state;
- }
- /* Display fast-forward notification, unless
- * disabled via override */
- if (!runloop_st->fastmotion_override.current.fastforward ||
- runloop_st->fastmotion_override.current.notification)
- {
- /* > Use widgets, if enabled */
- #if defined(HAVE_GFX_WIDGETS)
- if (widgets_active)
- {
- if (settings->bools.notification_show_fast_forward)
- {
- if (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
- video_st->flags |= VIDEO_FLAG_WIDGETS_FAST_FORWARD;
- else
- video_st->flags &= ~VIDEO_FLAG_WIDGETS_FAST_FORWARD;
- }
- else
- video_st->flags &= ~VIDEO_FLAG_WIDGETS_FAST_FORWARD;
- }
- else
- #endif
- {
- /* > If widgets are disabled, display fast-forward
- * status via OSD text for 1 frame every frame */
- if ( (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
- && settings->bools.notification_show_fast_forward)
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_FAST_FORWARD), 1, 1, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- #if defined(HAVE_GFX_WIDGETS)
- else
- video_st->flags &= ~VIDEO_FLAG_WIDGETS_FAST_FORWARD;
- #endif
- #ifdef HAVE_CHEEVOS
- if (!cheevos_hardcore_active)
- #endif
- {
- {
- /* Check slowmotion hotkeys */
- static bool old_slowmotion_button_state = false;
- static bool old_slowmotion_hold_button_state = false;
- bool new_slowmotion_button_state = BIT256_GET(
- current_bits, RARCH_SLOWMOTION_KEY);
- bool new_slowmotion_hold_button_state = BIT256_GET(
- current_bits, RARCH_SLOWMOTION_HOLD_KEY);
- /* Don't allow slowmotion while paused */
- if (runloop_paused)
- {
- new_slowmotion_button_state = false;
- new_slowmotion_hold_button_state = false;
- }
- if (new_slowmotion_button_state && !old_slowmotion_button_state)
- {
- if (!(runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION))
- runloop_st->flags |= RUNLOOP_FLAG_SLOWMOTION;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_SLOWMOTION;
- }
- else if (old_slowmotion_hold_button_state != new_slowmotion_hold_button_state)
- {
- if (new_slowmotion_hold_button_state)
- runloop_st->flags |= RUNLOOP_FLAG_SLOWMOTION;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_SLOWMOTION;
- }
- if (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
- {
- if (settings->uints.video_black_frame_insertion)
- if (!(runloop_st->flags & RUNLOOP_FLAG_IDLE))
- video_driver_cached_frame();
- #if defined(HAVE_GFX_WIDGETS)
- if (!widgets_active)
- #endif
- {
- #ifdef HAVE_REWIND
- struct state_manager_rewind_state
- *rewind_st = &runloop_st->rewind_st;
- if (rewind_st->flags
- & STATE_MGR_REWIND_ST_FLAG_FRAME_IS_REVERSED)
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_SLOW_MOTION_REWIND), 1, 1, false, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- else
- #endif
- runloop_msg_queue_push(
- msg_hash_to_str(MSG_SLOW_MOTION), 1, 1, false,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- }
- old_slowmotion_button_state = new_slowmotion_button_state;
- old_slowmotion_hold_button_state = new_slowmotion_hold_button_state;
- }
- }
- /* Check save state slot hotkeys */
- {
- static bool old_should_slot_increase = false;
- static bool old_should_slot_decrease = false;
- bool should_slot_increase = BIT256_GET(
- current_bits, RARCH_STATE_SLOT_PLUS);
- bool should_slot_decrease = BIT256_GET(
- current_bits, RARCH_STATE_SLOT_MINUS);
- bool check1 = true;
- bool check2 = should_slot_increase && !old_should_slot_increase;
- int addition = 1;
- int state_slot = settings->ints.state_slot;
- if (!check2)
- {
- check2 = should_slot_decrease && !old_should_slot_decrease;
- check1 = state_slot > -1;
- addition = -1;
- /* Wrap-around to 999 */
- if (check2 && !check1 && state_slot + addition < -1)
- {
- state_slot = 1000;
- check1 = true;
- }
- }
- /* Wrap-around to -1 (Auto) */
- else if (state_slot + addition > 999)
- state_slot = -2;
- if (check2)
- {
- size_t _len;
- char msg[128];
- int cur_state_slot = state_slot + addition;
- if (check1)
- configuration_set_int(settings, settings->ints.state_slot,
- cur_state_slot);
- _len = strlcpy(msg, msg_hash_to_str(MSG_STATE_SLOT), sizeof(msg));
- snprintf(msg + _len, sizeof(msg) - _len,
- ": %d", settings->ints.state_slot);
- if (cur_state_slot < 0)
- strlcat(msg, " (Auto)", sizeof(msg));
- #ifdef HAVE_GFX_WIDGETS
- if (dispwidget_get_ptr()->active)
- gfx_widget_set_generic_message(msg, 1000);
- else
- #endif
- runloop_msg_queue_push(msg, 2, 60, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("[State]: %s\n", msg);
- }
- old_should_slot_increase = should_slot_increase;
- old_should_slot_decrease = should_slot_decrease;
- }
- /* Check replay slot hotkeys */
- {
- static bool old_should_replay_slot_increase = false;
- static bool old_should_replay_slot_decrease = false;
- bool should_slot_increase = BIT256_GET(
- current_bits, RARCH_REPLAY_SLOT_PLUS);
- bool should_slot_decrease = BIT256_GET(
- current_bits, RARCH_REPLAY_SLOT_MINUS);
- bool check1 = true;
- bool check2 = should_slot_increase && !old_should_replay_slot_increase;
- int addition = 1;
- int replay_slot = settings->ints.replay_slot;
- if (!check2)
- {
- check2 = should_slot_decrease && !old_should_replay_slot_decrease;
- check1 = replay_slot > -1;
- addition = -1;
- /* Wrap-around to 999 */
- if (check2 && !check1 && replay_slot + addition < -1)
- {
- replay_slot = 1000;
- check1 = true;
- }
- }
- /* Wrap-around to -1 (Auto) */
- else if (replay_slot + addition > 999)
- replay_slot = -2;
- if (check2)
- {
- size_t _len;
- char msg[128];
- int cur_replay_slot = replay_slot + addition;
- if (check1)
- configuration_set_int(settings, settings->ints.replay_slot,
- cur_replay_slot);
- _len = strlcpy(msg, msg_hash_to_str(MSG_REPLAY_SLOT), sizeof(msg));
- snprintf(msg + _len, sizeof(msg) - _len,
- ": %d", settings->ints.replay_slot);
- if (cur_replay_slot < 0)
- strlcat(msg, " (Auto)", sizeof(msg));
- #ifdef HAVE_GFX_WIDGETS
- if (dispwidget_get_ptr()->active)
- gfx_widget_set_generic_message(msg, 1000);
- else
- #endif
- runloop_msg_queue_push(msg, 2, 60, true, NULL,
- MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_LOG("[Replay]: %s\n", msg);
- }
- old_should_replay_slot_increase = should_slot_increase;
- old_should_replay_slot_decrease = should_slot_decrease;
- }
- /* Check save state hotkeys */
- HOTKEY_CHECK(RARCH_SAVE_STATE_KEY, CMD_EVENT_SAVE_STATE, true, NULL);
- HOTKEY_CHECK(RARCH_LOAD_STATE_KEY, CMD_EVENT_LOAD_STATE, true, NULL);
- /* Check reset hotkey */
- HOTKEY_CHECK(RARCH_RESET, CMD_EVENT_RESET, true, NULL);
- /* Check VRR runloop hotkey */
- HOTKEY_CHECK(RARCH_VRR_RUNLOOP_TOGGLE, CMD_EVENT_VRR_RUNLOOP_TOGGLE, true, NULL);
- /* Check bsv movie hotkeys */
- HOTKEY_CHECK(RARCH_PLAY_REPLAY_KEY, CMD_EVENT_PLAY_REPLAY, true, NULL);
- HOTKEY_CHECK(RARCH_RECORD_REPLAY_KEY, CMD_EVENT_RECORD_REPLAY, true, NULL);
- HOTKEY_CHECK(RARCH_HALT_REPLAY_KEY, CMD_EVENT_HALT_REPLAY, true, NULL);
- /* Check Disc Control hotkeys */
- HOTKEY_CHECK3(
- RARCH_DISK_EJECT_TOGGLE, CMD_EVENT_DISK_EJECT_TOGGLE,
- RARCH_DISK_NEXT, CMD_EVENT_DISK_NEXT,
- RARCH_DISK_PREV, CMD_EVENT_DISK_PREV);
- /* Check cheat hotkeys */
- HOTKEY_CHECK3(
- RARCH_CHEAT_INDEX_PLUS, CMD_EVENT_CHEAT_INDEX_PLUS,
- RARCH_CHEAT_INDEX_MINUS, CMD_EVENT_CHEAT_INDEX_MINUS,
- RARCH_CHEAT_TOGGLE, CMD_EVENT_CHEAT_TOGGLE);
- #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
- /* Check shader hotkeys */
- HOTKEY_CHECK3(
- RARCH_SHADER_NEXT, CMD_EVENT_SHADER_NEXT,
- RARCH_SHADER_PREV, CMD_EVENT_SHADER_PREV,
- RARCH_SHADER_TOGGLE, CMD_EVENT_SHADER_TOGGLE);
- if (settings->bools.video_shader_watch_files)
- {
- static rarch_timer_t timer = {0};
- static bool need_to_apply = false;
- if (video_shader_check_for_changes())
- {
- need_to_apply = true;
- if (!timer.timer_begin)
- {
- timer.timeout_us = SHADER_FILE_WATCH_DELAY_MSEC * 1000;
- timer.current = cpu_features_get_time_usec();
- timer.timeout_end = timer.current + timer.timeout_us;
- timer.timer_begin = true;
- timer.timer_end = false;
- }
- }
- /* If a file is modified atomically (moved/renamed from a different file),
- * we have no idea how long that might take.
- * If we're trying to re-apply shaders immediately after changes are made
- * to the original file(s), the filesystem might be in an in-between
- * state where the new file hasn't been moved over yet and the original
- * file was already deleted. This leaves us no choice but to wait an
- * arbitrary amount of time and hope for the best.
- */
- if (need_to_apply)
- {
- timer.current = current_time;
- timer.timeout_us = timer.timeout_end - timer.current;
- if ( !timer.timer_end
- && timer.timeout_us <= 0)
- {
- timer.timer_end = true;
- timer.timer_begin = false;
- timer.timeout_end = 0;
- need_to_apply = false;
- command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
- }
- }
- }
- if ( settings->uints.video_shader_delay
- && !runloop_st->shader_delay_timer.timer_end)
- {
- if (!runloop_st->shader_delay_timer.timer_begin)
- {
- runloop_st->shader_delay_timer.timeout_us = settings->uints.video_shader_delay * 1000;
- runloop_st->shader_delay_timer.current = cpu_features_get_time_usec();
- runloop_st->shader_delay_timer.timeout_end = runloop_st->shader_delay_timer.current
- + runloop_st->shader_delay_timer.timeout_us;
- runloop_st->shader_delay_timer.timer_begin = true;
- runloop_st->shader_delay_timer.timer_end = false;
- }
- else
- {
- runloop_st->shader_delay_timer.current = current_time;
- runloop_st->shader_delay_timer.timeout_us = runloop_st->shader_delay_timer.timeout_end
- - runloop_st->shader_delay_timer.current;
- if (runloop_st->shader_delay_timer.timeout_us <= 0)
- {
- runloop_st->shader_delay_timer.timer_end = true;
- runloop_st->shader_delay_timer.timer_begin = false;
- runloop_st->shader_delay_timer.timeout_end = 0;
- {
- const char *preset = video_shader_get_current_shader_preset();
- enum rarch_shader_type type = video_shader_parse_type(preset);
- video_shader_apply_shader(settings, type, preset, false);
- }
- }
- }
- }
- #endif
- if (runloop_paused)
- {
- cbs->poll_cb();
- return RUNLOOP_STATE_PAUSE;
- }
- return RUNLOOP_STATE_ITERATE;
- }
- /**
- * runloop_iterate:
- *
- * Run Libretro core in RetroArch for one frame.
- *
- * Returns: 0 on success, 1 if we have to wait until
- * button input in order to wake up the loop,
- * -1 if we forcibly quit out of the RetroArch iteration loop.
- **/
- int runloop_iterate(void)
- {
- int i;
- enum analog_dpad_mode dpad_mode[MAX_USERS];
- input_driver_state_t *input_st = input_state_get_ptr();
- audio_driver_state_t *audio_st = audio_state_get_ptr();
- video_driver_state_t *video_st = video_state_get_ptr();
- recording_state_t *recording_st = recording_state_get_ptr();
- camera_driver_state_t *camera_st = camera_state_get_ptr();
- #if defined(HAVE_COCOATOUCH)
- uico_driver_state_t *uico_st = uico_state_get_ptr();
- #endif
- settings_t *settings = config_get_ptr();
- runloop_state_t *runloop_st = &runloop_state;
- unsigned video_frame_delay = settings->uints.video_frame_delay;
- unsigned video_frame_delay_effective = video_st->frame_delay_effective;
- bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
- unsigned max_users = settings->uints.input_max_users;
- retro_time_t current_time = cpu_features_get_time_usec();
- #ifdef HAVE_MENU
- #ifdef HAVE_NETWORKING
- bool menu_pause_libretro = settings->bools.menu_pause_libretro &&
- netplay_driver_ctl(RARCH_NETPLAY_CTL_ALLOW_PAUSE, NULL);
- #else
- bool menu_pause_libretro = settings->bools.menu_pause_libretro;
- #endif
- bool core_paused =
- (runloop_st->flags & RUNLOOP_FLAG_PAUSED) ||
- (menu_pause_libretro && (menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE));
- #else
- bool core_paused = (runloop_st->flags & RUNLOOP_FLAG_PAUSED);
- #endif
- float slowmotion_ratio = settings->floats.slowmotion_ratio;
- #ifdef HAVE_CHEEVOS
- bool cheevos_enable = settings->bools.cheevos_enable;
- #endif
- bool audio_sync = settings->bools.audio_sync;
- #ifdef HAVE_DISCORD
- discord_state_t *discord_st = discord_state_get_ptr();
- if (discord_st->inited)
- {
- Discord_RunCallbacks();
- #ifdef DISCORD_DISABLE_IO_THREAD
- Discord_UpdateConnection();
- #endif
- }
- #endif
- bool is_need_reinit_drivers = (runloop_st->flags2 & RUNLOOP_FLAG_NEED_REINIT_DRIVERS);
- if (is_need_reinit_drivers) {
- command_event(CMD_EVENT_REINIT, NULL);
- runloop_st->flags2 &= ~RUNLOOP_FLAG_NEED_REINIT_DRIVERS;
- }
- if (runloop_st->frame_time.callback)
- {
- /* Updates frame timing if frame timing callback is in use by the core.
- * Limits frame time if fast forward ratio throttle is enabled. */
- retro_usec_t runloop_last_frame_time = runloop_st->frame_time_last;
- retro_time_t current = current_time;
- bool is_locked_fps = (
- (runloop_st->flags & RUNLOOP_FLAG_PAUSED)
- || (input_st->flags & INP_FLAG_NONBLOCKING))
- | !!recording_st->data;
- retro_time_t delta = (!runloop_last_frame_time || is_locked_fps)
- ? runloop_st->frame_time.reference
- : (current - runloop_last_frame_time);
- if (is_locked_fps)
- runloop_st->frame_time_last = 0;
- else
- {
- runloop_st->frame_time_last = current;
- if (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
- delta /= slowmotion_ratio;
- }
- if (!core_paused)
- runloop_st->frame_time.callback(delta);
- }
- /* Update audio buffer occupancy if buffer status
- * callback is in use by the core */
- if (runloop_st->audio_buffer_status.callback)
- {
- bool audio_buf_active = false;
- unsigned audio_buf_occupancy = 0;
- bool audio_buf_underrun = false;
- if (!( (runloop_st->flags & RUNLOOP_FLAG_PAUSED)
- || !(audio_st->flags & AUDIO_FLAG_ACTIVE)
- || !(audio_st->output_samples_buf))
- && audio_st->current_audio->write_avail
- && audio_st->context_audio_data
- && audio_st->buffer_size)
- {
- size_t audio_buf_avail;
- if ((audio_buf_avail = audio_st->current_audio->write_avail(
- audio_st->context_audio_data)) > audio_st->buffer_size)
- audio_buf_avail = audio_st->buffer_size;
- audio_buf_occupancy = (unsigned)(100 - (audio_buf_avail * 100) /
- audio_st->buffer_size);
- /* Elsewhere, we standardise on a 'low water mark'
- * of 25% of the total audio buffer size - use
- * the same metric here (can be made more sophisticated
- * if required - i.e. determine buffer occupancy in
- * terms of usec, and weigh this against the expected
- * frame time) */
- audio_buf_underrun = audio_buf_occupancy < 25;
- audio_buf_active = true;
- }
- if (!core_paused)
- runloop_st->audio_buffer_status.callback(
- audio_buf_active, audio_buf_occupancy, audio_buf_underrun);
- }
- switch ((enum runloop_state_enum)runloop_check_state(
- global_get_ptr()->error_on_init,
- settings, current_time))
- {
- case RUNLOOP_STATE_QUIT:
- runloop_st->frame_limit_last_time = 0.0;
- runloop_st->flags &= ~RUNLOOP_FLAG_CORE_RUNNING;
- command_event(CMD_EVENT_QUIT, NULL);
- return -1;
- case RUNLOOP_STATE_POLLED_AND_SLEEP:
- #ifdef HAVE_NETWORKING
- /* FIXME: This is an ugly way to tell Netplay this... */
- netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL);
- #endif
- #if defined(HAVE_COCOATOUCH)
- if (!(uico_st->flags & UICO_ST_FLAG_IS_ON_FOREGROUND))
- #endif
- retro_sleep(10);
- return 1;
- case RUNLOOP_STATE_PAUSE:
- #ifdef HAVE_NETWORKING
- /* FIXME: This is an ugly way to tell Netplay this... */
- netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL);
- #endif
- video_driver_cached_frame();
- return 1;
- case RUNLOOP_STATE_END:
- #ifdef HAVE_NETWORKING
- #ifdef HAVE_MENU
- /* FIXME: This is an ugly way to tell Netplay this... */
- if (menu_pause_libretro &&
- netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL)
- )
- netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL);
- #endif
- #endif
- goto end;
- case RUNLOOP_STATE_MENU_ITERATE:
- #ifdef HAVE_NETWORKING
- /* FIXME: This is an ugly way to tell Netplay this... */
- netplay_driver_ctl(RARCH_NETPLAY_CTL_PAUSE, NULL);
- #endif
- return 0;
- case RUNLOOP_STATE_ITERATE:
- runloop_st->flags |= RUNLOOP_FLAG_CORE_RUNNING;
- break;
- }
- #ifdef HAVE_THREADS
- if (runloop_st->flags & RUNLOOP_FLAG_AUTOSAVE)
- autosave_lock();
- #endif
- #ifdef HAVE_BSV_MOVIE
- bsv_movie_next_frame(input_st);
- #endif
- if ( camera_st->cb.caps
- && camera_st->driver
- && camera_st->driver->poll
- && camera_st->data)
- camera_st->driver->poll(camera_st->data,
- camera_st->cb.frame_raw_framebuffer,
- camera_st->cb.frame_opengl_texture);
- /* Update binds for analog dpad modes. */
- for (i = 0; i < (int)max_users; i++)
- {
- dpad_mode[i] = (enum analog_dpad_mode)
- settings->uints.input_analog_dpad_mode[i];
- switch (dpad_mode[i])
- {
- case ANALOG_DPAD_LSTICK:
- case ANALOG_DPAD_RSTICK:
- {
- unsigned mapped_port = settings->uints.input_remap_ports[i];
- if (input_st->analog_requested[mapped_port])
- dpad_mode[i] = ANALOG_DPAD_NONE;
- }
- break;
- case ANALOG_DPAD_LSTICK_FORCED:
- dpad_mode[i] = ANALOG_DPAD_LSTICK;
- break;
- case ANALOG_DPAD_RSTICK_FORCED:
- dpad_mode[i] = ANALOG_DPAD_RSTICK;
- break;
- default:
- break;
- }
- /* Push analog to D-Pad mappings to binds. */
- if (dpad_mode[i] != ANALOG_DPAD_NONE)
- {
- unsigned k;
- unsigned joy_idx = settings->uints.input_joypad_index[i];
- struct retro_keybind *general_binds = input_config_binds[joy_idx];
- struct retro_keybind *auto_binds = input_autoconf_binds[joy_idx];
- unsigned x_plus = RARCH_ANALOG_RIGHT_X_PLUS;
- unsigned y_plus = RARCH_ANALOG_RIGHT_Y_PLUS;
- unsigned x_minus = RARCH_ANALOG_RIGHT_X_MINUS;
- unsigned y_minus = RARCH_ANALOG_RIGHT_Y_MINUS;
- if (dpad_mode[i] == ANALOG_DPAD_LSTICK)
- {
- x_plus = RARCH_ANALOG_LEFT_X_PLUS;
- y_plus = RARCH_ANALOG_LEFT_Y_PLUS;
- x_minus = RARCH_ANALOG_LEFT_X_MINUS;
- y_minus = RARCH_ANALOG_LEFT_Y_MINUS;
- }
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- {
- (auto_binds)[k].orig_joyaxis = (auto_binds)[k].joyaxis;
- (general_binds)[k].orig_joyaxis = (general_binds)[k].joyaxis;
- }
- if (!INHERIT_JOYAXIS(auto_binds))
- {
- unsigned j = x_plus + 3;
- /* Inherit joyaxis from analogs. */
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- (auto_binds)[k].joyaxis = (auto_binds)[j--].joyaxis;
- }
- if (!INHERIT_JOYAXIS(general_binds))
- {
- unsigned j = x_plus + 3;
- /* Inherit joyaxis from analogs. */
- for (k = RETRO_DEVICE_ID_JOYPAD_UP; k <= RETRO_DEVICE_ID_JOYPAD_RIGHT; k++)
- (general_binds)[k].joyaxis = (general_binds)[j--].joyaxis;
- }
- }
- }
- /* Frame delay */
- if ( !(input_st->flags & INP_FLAG_NONBLOCKING)
- || (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION))
- {
- bool skip_delay = core_paused
- || (runloop_st->flags & RUNLOOP_FLAG_SLOWMOTION)
- || (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION);
- if (settings->bools.video_frame_delay_auto)
- {
- float refresh_rate = settings->floats.video_refresh_rate;
- uint8_t video_swap_interval = runloop_get_video_swap_interval(
- settings->uints.video_swap_interval);
- uint8_t video_bfi = settings->uints.video_black_frame_insertion;
- uint8_t frame_time_interval = 8;
- static uint8_t skip_update = 0;
- static bool skip_delay_prev = false;
- bool frame_time_update =
- /* Skip some initial frames for stabilization */
- video_st->frame_count > frame_time_interval &&
- /* Only update when there are enough frames for averaging */
- video_st->frame_count % frame_time_interval == 0;
- /* A few frames must be ignored after slow+fastmotion/pause
- * is disabled or geometry change is triggered */
- if ( (!skip_delay && skip_delay_prev)
- || video_st->frame_delay_pause)
- {
- skip_update = frame_time_interval * 4;
- video_st->frame_delay_pause = false;
- }
- if (skip_update)
- skip_update--;
- skip_delay_prev = skip_delay;
- /* Always skip when slow+fastmotion/pause is active */
- if (skip_delay_prev)
- skip_update = 1;
- if (skip_update)
- frame_time_update = false;
- /* Black frame insertion + swap interval multiplier */
- refresh_rate = (refresh_rate / (video_bfi + 1.0f) / video_swap_interval);
- /* Set target moderately as half frame time with 0 (Auto) delay */
- if (video_frame_delay == 0)
- video_frame_delay = 1 / refresh_rate * 1000 / 2;
- /* Reset new desired delay target */
- if (video_st->frame_delay_target != video_frame_delay)
- {
- frame_time_update = false;
- video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay;
- RARCH_LOG("[Video]: Frame delay reset to %d ms.\n", video_frame_delay);
- }
- /* Decide what should happen to effective delay */
- if (video_frame_delay_effective > 0 && frame_time_update)
- {
- video_frame_delay_auto_t vfda = {0};
- vfda.frame_time_interval = frame_time_interval;
- vfda.refresh_rate = refresh_rate;
- video_frame_delay_auto(video_st, &vfda);
- if (vfda.delay_decrease > 0)
- {
- video_frame_delay_effective -= vfda.delay_decrease;
- RARCH_LOG("[Video]: Frame delay decrease by %d ms to %d ms due to frame time average: %d > %d.\n",
- vfda.delay_decrease, video_frame_delay_effective, vfda.frame_time_avg, vfda.frame_time_target);
- }
- }
- }
- else
- video_st->frame_delay_target = video_frame_delay_effective = video_frame_delay;
- video_st->frame_delay_effective = video_frame_delay_effective;
- /* Never apply frame delay when slow+fastmotion/pause is active */
- if (video_frame_delay_effective > 0 && !skip_delay)
- retro_sleep(video_frame_delay_effective);
- }
- {
- #ifdef HAVE_RUNAHEAD
- bool run_ahead_enabled = settings->bools.run_ahead_enabled;
- unsigned run_ahead_num_frames = settings->uints.run_ahead_frames;
- bool run_ahead_hide_warnings = settings->bools.run_ahead_hide_warnings;
- bool run_ahead_secondary_instance = settings->bools.run_ahead_secondary_instance;
- /* Run Ahead Feature replaces the call to core_run in this loop */
- bool want_runahead = run_ahead_enabled
- && (run_ahead_num_frames > 0)
- && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_AVAILABLE);
- #ifdef HAVE_NETWORKING
- want_runahead = want_runahead
- && !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL);
- #endif
- if (want_runahead)
- runahead_run(
- runloop_st,
- run_ahead_num_frames,
- run_ahead_hide_warnings,
- run_ahead_secondary_instance);
- else if (runloop_st->preempt_data)
- preempt_run(runloop_st->preempt_data, runloop_st);
- else
- #endif
- core_run();
- }
- /* Increment runtime tick counter after each call to
- * core_run() or run_ahead() */
- runloop_st->core_runtime_usec += runloop_core_runtime_tick(
- runloop_st,
- slowmotion_ratio,
- current_time);
- #ifdef HAVE_CHEEVOS
- if (cheevos_enable)
- rcheevos_test();
- #endif
- #ifdef HAVE_CHEATS
- cheat_manager_apply_retro_cheats();
- #endif
- #ifdef HAVE_PRESENCE
- presence_update(PRESENCE_GAME);
- #endif
- /* Restores analog D-pad binds temporarily overridden. */
- for (i = 0; i < (int)max_users; i++)
- {
- if (dpad_mode[i] != ANALOG_DPAD_NONE)
- {
- int j;
- unsigned joy_idx = settings->uints.input_joypad_index[i];
- struct retro_keybind *general_binds = input_config_binds[joy_idx];
- struct retro_keybind *auto_binds = input_autoconf_binds[joy_idx];
- for (j = RETRO_DEVICE_ID_JOYPAD_UP; j <= RETRO_DEVICE_ID_JOYPAD_RIGHT; j++)
- {
- (auto_binds)[j].joyaxis = (auto_binds)[j].orig_joyaxis;
- (general_binds)[j].joyaxis = (general_binds)[j].orig_joyaxis;
- }
- }
- }
- #ifdef HAVE_BSV_MOVIE
- bsv_movie_finish_rewind(input_st);
- if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
- {
- movie_stop_playback(input_st);
- command_event(CMD_EVENT_PAUSE, NULL);
- }
- if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
- {
- movie_stop_playback(input_st);
- command_event(CMD_EVENT_PAUSE, NULL);
- }
- #endif
- #ifdef HAVE_THREADS
- if (runloop_st->flags & RUNLOOP_FLAG_AUTOSAVE)
- autosave_unlock();
- #endif
- end:
- if (vrr_runloop_enable)
- {
- /* Sync on video only, block audio later. */
- if (runloop_st->fastforward_after_frames && audio_sync)
- {
- if (runloop_st->fastforward_after_frames == 1)
- {
- /* Nonblocking audio */
- if ( (audio_st->flags & AUDIO_FLAG_ACTIVE)
- && (audio_st->context_audio_data))
- audio_st->current_audio->set_nonblock_state(
- audio_st->context_audio_data, true);
- audio_st->chunk_size =
- audio_st->chunk_nonblock_size;
- }
- runloop_st->fastforward_after_frames++;
- if (runloop_st->fastforward_after_frames == 6)
- {
- /* Blocking audio */
- if ( (audio_st->flags & AUDIO_FLAG_ACTIVE)
- && (audio_st->context_audio_data))
- audio_st->current_audio->set_nonblock_state(
- audio_st->context_audio_data,
- audio_sync ? false : true);
- audio_st->chunk_size = audio_st->chunk_block_size;
- runloop_st->fastforward_after_frames = 0;
- }
- }
- if (runloop_st->flags & RUNLOOP_FLAG_FASTMOTION)
- runloop_set_frame_limit(&video_st->av_info,
- runloop_get_fastforward_ratio(settings,
- &runloop_st->fastmotion_override.current));
- else
- runloop_set_frame_limit(&video_st->av_info,
- 1.0f);
- }
- /* if there's a fast forward limit, inject sleeps to keep from going too fast. */
- if (runloop_st->frame_limit_minimum_time)
- {
- const retro_time_t end_frame_time = cpu_features_get_time_usec();
- const retro_time_t to_sleep_ms = (
- ( runloop_st->frame_limit_last_time
- + runloop_st->frame_limit_minimum_time)
- - end_frame_time) / 1000;
- if (to_sleep_ms > 0)
- {
- unsigned sleep_ms = (unsigned)to_sleep_ms;
- /* Combat jitter a bit. */
- runloop_st->frame_limit_last_time +=
- runloop_st->frame_limit_minimum_time;
- if (sleep_ms > 0)
- {
- #if defined(HAVE_COCOATOUCH)
- if (!(uico_state_get_ptr()->flags & UICO_ST_FLAG_IS_ON_FOREGROUND))
- #endif
- retro_sleep(sleep_ms);
- }
- return 1;
- }
- runloop_st->frame_limit_last_time = end_frame_time;
- }
- /* Set paused state after x frames */
- if (runloop_st->run_frames_and_pause > 0)
- {
- runloop_st->run_frames_and_pause--;
- if (!runloop_st->run_frames_and_pause)
- runloop_st->flags |= RUNLOOP_FLAG_PAUSED;
- }
- return 0;
- }
- void runloop_msg_queue_deinit(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
- msg_queue_deinitialize(&runloop_st->msg_queue);
- RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
- #ifdef HAVE_THREADS
- slock_free(runloop_st->msg_queue_lock);
- runloop_st->msg_queue_lock = NULL;
- #endif
- runloop_st->msg_queue_size = 0;
- }
- void runloop_msg_queue_init(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- runloop_msg_queue_deinit();
- msg_queue_initialize(&runloop_st->msg_queue, 8);
- #ifdef HAVE_THREADS
- runloop_st->msg_queue_lock = slock_new();
- #endif
- }
- void runloop_task_msg_queue_push(
- retro_task_t *task, const char *msg,
- unsigned prio, unsigned duration,
- bool flush)
- {
- #if defined(HAVE_GFX_WIDGETS)
- #ifdef HAVE_MENU
- struct menu_state *menu_st = menu_state_get_ptr();
- #endif
- #ifdef HAVE_ACCESSIBILITY
- access_state_t *access_st = access_state_get_ptr();
- settings_t *settings = config_get_ptr();
- bool accessibility_enable = settings->bools.accessibility_enable;
- unsigned accessibility_narrator_speech_speed = settings->uints.accessibility_narrator_speech_speed;
- #endif
- runloop_state_t *runloop_st = &runloop_state;
- dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr();
- bool widgets_active = p_dispwidget->active;
- if (widgets_active && task->title && !task->mute)
- {
- RUNLOOP_MSG_QUEUE_LOCK(runloop_st);
- ui_companion_driver_msg_queue_push(msg,
- prio, task ? duration : duration * 60 / 1000, flush);
- #ifdef HAVE_ACCESSIBILITY
- if (is_accessibility_enabled(
- accessibility_enable,
- access_st->enabled))
- accessibility_speak_priority(
- accessibility_enable,
- accessibility_narrator_speech_speed,
- (char*)msg, 0);
- #endif
- gfx_widgets_msg_queue_push(
- task,
- msg,
- duration,
- NULL,
- (enum message_queue_icon)MESSAGE_QUEUE_CATEGORY_INFO,
- (enum message_queue_category)MESSAGE_QUEUE_ICON_DEFAULT,
- prio,
- flush,
- #ifdef HAVE_MENU
- menu_st->flags & MENU_ST_FLAG_ALIVE
- #else
- false
- #endif
- );
- RUNLOOP_MSG_QUEUE_UNLOCK(runloop_st);
- }
- else
- #endif
- runloop_msg_queue_push(msg, prio, duration, flush, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- }
- bool runloop_get_current_savestate_path(char *path, size_t len)
- {
- settings_t *settings = config_get_ptr();
- int state_slot = settings ? settings->ints.state_slot : 0;
- return runloop_get_savestate_path(path, len, state_slot);
- }
- bool runloop_get_savestate_path(char *path, size_t len, int state_slot)
- {
- runloop_state_t *runloop_st = &runloop_state;
- const char *name_savestate = NULL;
- if (!path)
- return false;
- name_savestate = runloop_st->name.savestate;
- if (string_is_empty(name_savestate))
- return false;
- if (state_slot < 0)
- fill_pathname_join_delim(path, name_savestate, "auto", '.', len);
- else
- {
- size_t _len = strlcpy(path, name_savestate, len);
- if (state_slot > 0)
- snprintf(path + _len, len - _len, "%d", state_slot);
- }
- return true;
- }
- bool runloop_get_current_replay_path(char *path, size_t len)
- {
- settings_t *settings = config_get_ptr();
- int slot = settings ? settings->ints.replay_slot : 0;
- return runloop_get_replay_path(path, len, slot);
- }
- bool runloop_get_replay_path(char *path, size_t len, unsigned slot)
- {
- size_t _len;
- runloop_state_t *runloop_st = &runloop_state;
- const char *name_replay = NULL;
- if (!path)
- return false;
- name_replay = runloop_st->name.replay;
- if (string_is_empty(name_replay))
- return false;
- _len = strlcpy(path, name_replay, len);
- if (slot >= 0)
- snprintf(path + _len, len - _len, "%d", slot);
- return true;
- }
- bool runloop_get_entry_state_path(char *path, size_t len, unsigned slot)
- {
- size_t _len;
- runloop_state_t *runloop_st = &runloop_state;
- const char *name_savestate = NULL;
- if (!path || !slot)
- return false;
- name_savestate = runloop_st->name.savestate;
- if (string_is_empty(name_savestate))
- return false;
- _len = strlcpy(path, name_savestate, len);
- snprintf(path + _len, len - _len, "%d.entry", slot);
- return true;
- }
- void runloop_set_current_core_type(
- enum rarch_core_type type, bool explicitly_set)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (runloop_st->flags & RUNLOOP_FLAG_HAS_SET_CORE)
- return;
- if (explicitly_set)
- {
- runloop_st->flags |= RUNLOOP_FLAG_HAS_SET_CORE;
- runloop_st->explicit_current_core_type = type;
- }
- runloop_st->current_core_type = type;
- }
- bool core_set_default_callbacks(void *data)
- {
- struct retro_callbacks *cbs = (struct retro_callbacks*)data;
- retro_input_state_t state_cb = core_input_state_poll_return_cb();
- cbs->frame_cb = video_driver_frame;
- cbs->sample_cb = audio_driver_sample;
- cbs->sample_batch_cb = audio_driver_sample_batch;
- cbs->state_cb = state_cb;
- cbs->poll_cb = input_driver_poll;
- return true;
- }
- #ifdef HAVE_NETWORKING
- /**
- * core_set_netplay_callbacks:
- *
- * Set the I/O callbacks to use netplay's interceding callback system. Should
- * only be called while initializing netplay.
- **/
- bool core_set_netplay_callbacks(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- /* Force normal poll type for netplay. */
- runloop_st->current_core.poll_type = POLL_TYPE_NORMAL;
- /* And use netplay's interceding callbacks */
- runloop_st->current_core.retro_set_video_refresh(video_frame_net);
- runloop_st->current_core.retro_set_audio_sample(audio_sample_net);
- runloop_st->current_core.retro_set_audio_sample_batch(audio_sample_batch_net);
- runloop_st->current_core.retro_set_input_state(input_state_net);
- return true;
- }
- /**
- * core_unset_netplay_callbacks
- *
- * Unset the I/O callbacks from having used netplay's interceding callback
- * system. Should only be called while uninitializing netplay.
- */
- bool core_unset_netplay_callbacks(void)
- {
- struct retro_callbacks cbs;
- runloop_state_t *runloop_st = &runloop_state;
- if (!core_set_default_callbacks(&cbs))
- return false;
- runloop_st->current_core.retro_set_video_refresh(cbs.frame_cb);
- runloop_st->current_core.retro_set_audio_sample(cbs.sample_cb);
- runloop_st->current_core.retro_set_audio_sample_batch(cbs.sample_batch_cb);
- runloop_st->current_core.retro_set_input_state(cbs.state_cb);
- return true;
- }
- #endif
- bool core_set_cheat(retro_ctx_cheat_info_t *info)
- {
- runloop_state_t *runloop_st = &runloop_state;
- #if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
- settings_t *settings = config_get_ptr();
- bool run_ahead_enabled = false;
- unsigned run_ahead_frames = 0;
- bool run_ahead_secondary_instance = false;
- bool want_runahead = false;
- if (settings)
- {
- run_ahead_enabled = settings->bools.run_ahead_enabled;
- run_ahead_frames = settings->uints.run_ahead_frames;
- run_ahead_secondary_instance = settings->bools.run_ahead_secondary_instance;
- want_runahead = run_ahead_enabled
- && (run_ahead_frames > 0)
- && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_AVAILABLE);
- #ifdef HAVE_NETWORKING
- if (want_runahead)
- want_runahead = !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL);
- #endif
- }
- #endif
- runloop_st->current_core.retro_cheat_set(info->index, info->enabled, info->code);
- #if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
- if ( (want_runahead)
- && (run_ahead_secondary_instance)
- && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE)
- && (secondary_core_ensure_exists(runloop_st, settings))
- && (runloop_st->secondary_core.retro_cheat_set))
- runloop_st->secondary_core.retro_cheat_set(
- info->index, info->enabled, info->code);
- #endif
- return true;
- }
- bool core_reset_cheat(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- #if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
- settings_t *settings = config_get_ptr();
- bool run_ahead_enabled = false;
- unsigned run_ahead_frames = 0;
- bool run_ahead_secondary_instance = false;
- bool want_runahead = false;
- if (settings)
- {
- run_ahead_enabled = settings->bools.run_ahead_enabled;
- run_ahead_frames = settings->uints.run_ahead_frames;
- run_ahead_secondary_instance = settings->bools.run_ahead_secondary_instance;
- want_runahead = run_ahead_enabled
- && (run_ahead_frames > 0)
- && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_AVAILABLE);
- #ifdef HAVE_NETWORKING
- if (want_runahead)
- want_runahead = !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL);
- #endif
- }
- #endif
- runloop_st->current_core.retro_cheat_reset();
- #if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
- if ( (want_runahead)
- && (run_ahead_secondary_instance)
- && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE)
- && (secondary_core_ensure_exists(runloop_st, settings))
- && (runloop_st->secondary_core.retro_cheat_reset))
- runloop_st->secondary_core.retro_cheat_reset();
- #endif
- return true;
- }
- bool core_set_poll_type(unsigned type)
- {
- runloop_state_t *runloop_st = &runloop_state;
- runloop_st->current_core.poll_type = type;
- return true;
- }
- bool core_set_controller_port_device(retro_ctx_controller_info_t *pad)
- {
- runloop_state_t *runloop_st = &runloop_state;
- input_driver_state_t *input_st = input_state_get_ptr();
- if (!pad)
- return false;
- /* We are potentially 'connecting' a entirely different
- * type of virtual input device, which may or may not
- * support analog inputs. We therefore have to reset
- * the 'analog input requested' flag for this port - but
- * since port mapping is arbitrary/mutable, it is easiest
- * to simply reset the flags for all ports.
- * Correct values will be registered at the next call
- * of 'input_state()' */
- memset(&input_st->analog_requested, 0,
- sizeof(input_st->analog_requested));
- #if defined(HAVE_RUNAHEAD)
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- runahead_remember_controller_port_device(runloop_st, pad->port, pad->device);
- #endif
- #endif
- runloop_st->current_core.retro_set_controller_port_device(pad->port, pad->device);
- return true;
- }
- bool core_get_memory(retro_ctx_memory_info_t *info)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!info)
- return false;
- info->size = runloop_st->current_core.retro_get_memory_size(info->id);
- info->data = runloop_st->current_core.retro_get_memory_data(info->id);
- return true;
- }
- bool core_load_game(retro_ctx_load_content_info_t *load_info)
- {
- bool game_loaded = false;
- runloop_state_t *runloop_st = &runloop_state;
- video_driver_set_cached_frame_ptr(NULL);
- #ifdef HAVE_RUNAHEAD
- runahead_set_load_content_info(runloop_st, load_info);
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- runahead_clear_controller_port_map(runloop_st);
- #endif
- #endif
- set_save_state_in_background(false);
- if (load_info && load_info->special)
- game_loaded = runloop_st->current_core.retro_load_game_special(
- load_info->special->id, load_info->info, load_info->content->size);
- else if (load_info && !string_is_empty(load_info->content->elems[0].data))
- game_loaded = runloop_st->current_core.retro_load_game(load_info->info);
- else if (content_get_flags() & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT)
- game_loaded = runloop_st->current_core.retro_load_game(NULL);
- if (game_loaded)
- {
- /* If 'game_loaded' is true at this point, then
- * core is actually running; register that any
- * changes to global remap-related parameters
- * should be reset once core is deinitialised */
- input_state_get_ptr()->flags |= INP_FLAG_REMAPPING_CACHE_ACTIVE;
- runloop_st->current_core.flags |= RETRO_CORE_FLAG_GAME_LOADED;
- return true;
- }
- runloop_st->current_core.flags &= ~RETRO_CORE_FLAG_GAME_LOADED;
- return false;
- }
- bool core_get_system_info(struct retro_system_info *system)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!system)
- return false;
- runloop_st->current_core.retro_get_system_info(system);
- return true;
- }
- bool core_unserialize(retro_ctx_serialize_info_t *info)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!info || !runloop_st->current_core.retro_unserialize(info->data_const, info->size))
- return false;
- #ifdef HAVE_NETWORKING
- netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info);
- #endif
- #if HAVE_RUNAHEAD
- command_event(CMD_EVENT_PREEMPT_RESET_BUFFER, NULL);
- #endif
- return true;
- }
- bool core_unserialize_special(retro_ctx_serialize_info_t *info)
- {
- bool ret;
- runloop_state_t *runloop_st = &runloop_state;
- if (!info)
- return false;
- runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- ret = runloop_st->current_core.retro_unserialize(info->data_const, info->size);
- runloop_st->flags &= ~RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- #ifdef HAVE_NETWORKING
- if (ret)
- netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, info);
- #endif
- return ret;
- }
- bool core_serialize(retro_ctx_serialize_info_t *info)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!info || !runloop_st->current_core.retro_serialize(info->data, info->size))
- return false;
- return true;
- }
- bool core_serialize_special(retro_ctx_serialize_info_t *info)
- {
- bool ret;
- runloop_state_t *runloop_st = &runloop_state;
- if (!info)
- return false;
- runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- ret = runloop_st->current_core.retro_serialize(
- info->data, info->size);
- runloop_st->flags &= ~RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- return ret;
- }
- bool core_serialize_size(retro_ctx_size_info_t *info)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!info)
- return false;
- info->size = runloop_st->current_core.retro_serialize_size();
- return true;
- }
- bool core_serialize_size_special(retro_ctx_size_info_t *info)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!info)
- return false;
- runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- info->size = runloop_st->current_core.retro_serialize_size();
- runloop_st->flags &= ~RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- return true;
- }
- uint64_t core_serialization_quirks(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- return runloop_st->current_core.serialization_quirks_v;
- }
- void core_reset(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- video_driver_set_cached_frame_ptr(NULL);
- runloop_st->current_core.retro_reset();
- }
- void core_run(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- struct retro_core_t *
- current_core = &runloop_st->current_core;
- const enum poll_type_override_t
- core_poll_type_override = runloop_st->core_poll_type_override;
- unsigned new_poll_type = (core_poll_type_override != POLL_TYPE_OVERRIDE_DONTCARE)
- ? (core_poll_type_override - 1)
- : current_core->poll_type;
- bool early_polling = new_poll_type == POLL_TYPE_EARLY;
- bool late_polling = new_poll_type == POLL_TYPE_LATE;
- #ifdef HAVE_NETWORKING
- bool netplay_preframe = netplay_driver_ctl(
- RARCH_NETPLAY_CTL_PRE_FRAME, NULL);
- if (!netplay_preframe)
- {
- /* Paused due to netplay. We must poll and display something so that a
- * netplay peer pausing doesn't just hang. */
- input_driver_poll();
- video_driver_cached_frame();
- return;
- }
- #endif
- if (early_polling)
- input_driver_poll();
- else if (late_polling)
- current_core->flags &= ~RETRO_CORE_FLAG_INPUT_POLLED;
- current_core->retro_run();
- if ( late_polling
- && (!(current_core->flags & RETRO_CORE_FLAG_INPUT_POLLED)))
- input_driver_poll();
- #ifdef HAVE_NETWORKING
- netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL);
- #endif
- }
- bool core_has_set_input_descriptor(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- return ((runloop_st->current_core.flags &
- RETRO_CORE_FLAG_HAS_SET_INPUT_DESCRIPTORS) > 0);
- }
- char *crt_switch_core_name(void)
- {
- return (char*)runloop_state.system.info.library_name;
- }
- void runloop_path_set_basename(const char *path)
- {
- runloop_state_t *runloop_st = &runloop_state;
- char *dst = NULL;
- path_set(RARCH_PATH_CONTENT, path);
- path_set(RARCH_PATH_BASENAME, path);
- #ifdef HAVE_COMPRESSION
- /* Removing extension is a bit tricky for compressed files.
- * Basename means:
- * /file/to/path/game.extension should be:
- * /file/to/path/game
- *
- * Two things to consider here are: /file/to/path/ is expected
- * to be a directory and "game" is a single file. This is used for
- * states and srm default paths.
- *
- * For compressed files we have:
- *
- * /file/to/path/comp.7z#game.extension and
- * /file/to/path/comp.7z#folder/game.extension
- *
- * The choice I take here is:
- * /file/to/path/game as basename. We might end up in a writable
- * directory then and the name of srm and states are meaningful.
- *
- */
- path_basedir_wrapper(runloop_st->runtime_content_path_basename);
- if (!string_is_empty(runloop_st->runtime_content_path_basename))
- fill_pathname_dir(runloop_st->runtime_content_path_basename, path, "", sizeof(runloop_st->runtime_content_path_basename));
- #endif
- if ((dst = strrchr(runloop_st->runtime_content_path_basename, '.')))
- *dst = '\0';
- }
- void runloop_path_set_names(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (!retroarch_override_setting_is_set(
- RARCH_OVERRIDE_SETTING_SAVE_PATH, NULL))
- {
- size_t len = strlcpy(runloop_st->name.savefile,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.savefile));
- runloop_st->name.savefile[len ] = '.';
- runloop_st->name.savefile[len+1] = 's';
- runloop_st->name.savefile[len+2] = 'r';
- runloop_st->name.savefile[len+3] = 'm';
- runloop_st->name.savefile[len+4] = '\0';
- }
- if (!retroarch_override_setting_is_set(
- RARCH_OVERRIDE_SETTING_STATE_PATH, NULL))
- {
- size_t len = strlcpy(
- runloop_st->name.savestate,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.savestate));
- runloop_st->name.savestate[len ] = '.';
- runloop_st->name.savestate[len+1] = 's';
- runloop_st->name.savestate[len+2] = 't';
- runloop_st->name.savestate[len+3] = 'a';
- runloop_st->name.savestate[len+4] = 't';
- runloop_st->name.savestate[len+5] = 'e';
- runloop_st->name.savestate[len+6] = '\0';
- }
- #ifdef HAVE_BSV_MOVIE
- if (!retroarch_override_setting_is_set(
- RARCH_OVERRIDE_SETTING_STATE_PATH, NULL))
- {
- size_t len = strlcpy(
- runloop_st->name.replay,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.replay));
- runloop_st->name.replay[len ] = '.';
- runloop_st->name.replay[len+1] = 'r';
- runloop_st->name.replay[len+2] = 'e';
- runloop_st->name.replay[len+3] = 'p';
- runloop_st->name.replay[len+4] = 'l';
- runloop_st->name.replay[len+5] = 'a';
- runloop_st->name.replay[len+6] = 'y';
- runloop_st->name.replay[len+7] = '\0';
- }
- #endif
- #ifdef HAVE_CHEATS
- if (!string_is_empty(runloop_st->runtime_content_path_basename))
- {
- size_t len = strlcpy(
- runloop_st->name.cheatfile,
- runloop_st->runtime_content_path_basename,
- sizeof(runloop_st->name.cheatfile));
- runloop_st->name.cheatfile[len ] = '.';
- runloop_st->name.cheatfile[len+1] = 'c';
- runloop_st->name.cheatfile[len+2] = 'h';
- runloop_st->name.cheatfile[len+3] = 't';
- runloop_st->name.cheatfile[len+4] = '\0';
- }
- #endif
- }
- void runloop_path_set_redirect(settings_t *settings,
- const char *old_savefile_dir,
- const char *old_savestate_dir)
- {
- char content_dir_name[PATH_MAX_LENGTH];
- char new_savefile_dir[PATH_MAX_LENGTH];
- char new_savestate_dir[PATH_MAX_LENGTH];
- runloop_state_t *runloop_st = &runloop_state;
- struct retro_system_info *system = &runloop_st->system.info;
- bool sort_savefiles_enable = settings->bools.sort_savefiles_enable;
- bool sort_savefiles_by_content_enable = settings->bools.sort_savefiles_by_content_enable;
- bool sort_savestates_enable = settings->bools.sort_savestates_enable;
- bool sort_savestates_by_content_enable = settings->bools.sort_savestates_by_content_enable;
- bool savefiles_in_content_dir = settings->bools.savefiles_in_content_dir;
- bool savestates_in_content_dir = settings->bools.savestates_in_content_dir;
- content_dir_name[0] = '\0';
- /* Initialize current save directories
- * with the values from the config. */
- strlcpy(new_savefile_dir, old_savefile_dir, sizeof(new_savefile_dir));
- strlcpy(new_savestate_dir, old_savestate_dir, sizeof(new_savestate_dir));
- /* Get content directory name, if per-content-directory
- * saves/states are enabled */
- if ((sort_savefiles_by_content_enable ||
- sort_savestates_by_content_enable) &&
- !string_is_empty(runloop_st->runtime_content_path_basename))
- fill_pathname_parent_dir_name(content_dir_name,
- runloop_st->runtime_content_path_basename,
- sizeof(content_dir_name));
- if (system && !string_is_empty(system->library_name))
- {
- #ifdef HAVE_MENU
- if (!string_is_equal(system->library_name,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE)))
- #endif
- {
- /* Per-core and/or per-content-directory saves */
- if ((sort_savefiles_enable || sort_savefiles_by_content_enable)
- && !string_is_empty(old_savefile_dir))
- {
- /* Append content directory name to save location */
- if (sort_savefiles_by_content_enable)
- fill_pathname_join_special(
- new_savefile_dir,
- old_savefile_dir,
- content_dir_name,
- sizeof(new_savefile_dir));
- /* Append library_name to the save location */
- if (sort_savefiles_enable)
- fill_pathname_join(
- new_savefile_dir,
- new_savefile_dir,
- system->library_name,
- sizeof(new_savefile_dir));
- /* If path doesn't exist, try to create it,
- * if everything fails revert to the original path. */
- if (!path_is_directory(new_savefile_dir))
- if (!path_mkdir(new_savefile_dir))
- {
- RARCH_LOG("%s %s\n",
- msg_hash_to_str(MSG_REVERTING_SAVEFILE_DIRECTORY_TO),
- old_savefile_dir);
- strlcpy(new_savefile_dir, old_savefile_dir, sizeof(new_savefile_dir));
- }
- }
- /* Per-core and/or per-content-directory savestates */
- if ((sort_savestates_enable || sort_savestates_by_content_enable)
- && !string_is_empty(old_savestate_dir))
- {
- /* Append content directory name to savestate location */
- if (sort_savestates_by_content_enable)
- fill_pathname_join_special(
- new_savestate_dir,
- old_savestate_dir,
- content_dir_name,
- sizeof(new_savestate_dir));
- /* Append library_name to the savestate location */
- if (sort_savestates_enable)
- {
- fill_pathname_join(
- new_savestate_dir,
- new_savestate_dir,
- system->library_name,
- sizeof(new_savestate_dir));
- }
- /* If path doesn't exist, try to create it.
- * If everything fails, revert to the original path. */
- if (!path_is_directory(new_savestate_dir))
- if (!path_mkdir(new_savestate_dir))
- {
- RARCH_LOG("%s %s\n",
- msg_hash_to_str(MSG_REVERTING_SAVESTATE_DIRECTORY_TO),
- old_savestate_dir);
- strlcpy(new_savestate_dir,
- old_savestate_dir,
- sizeof(new_savestate_dir));
- }
- }
- }
- }
- /* Set savefile directory if empty to content directory */
- if (string_is_empty(new_savefile_dir) || savefiles_in_content_dir)
- {
- strlcpy(new_savefile_dir,
- runloop_st->runtime_content_path_basename,
- sizeof(new_savefile_dir));
- path_basedir(new_savefile_dir);
- if (string_is_empty(new_savefile_dir))
- RARCH_LOG("Cannot resolve save file path.\n");
- else if (sort_savefiles_enable || sort_savefiles_by_content_enable)
- RARCH_LOG("Saving files in content directory is set. This overrides other save file directory settings.\n");
- }
- /* Set savestate directory if empty based on content directory */
- if (string_is_empty(new_savestate_dir) || savestates_in_content_dir)
- {
- strlcpy(new_savestate_dir,
- runloop_st->runtime_content_path_basename,
- sizeof(new_savestate_dir));
- path_basedir(new_savestate_dir);
- if (string_is_empty(new_savestate_dir))
- RARCH_LOG("Cannot resolve save state file path.\n");
- else if (sort_savestates_enable || sort_savestates_by_content_enable)
- RARCH_LOG("Saving save states in content directory is set. This overrides other save state file directory settings.\n");
- }
- #ifdef HAVE_NETWORKING
- /* Special save directory for netplay clients. */
- if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) &&
- !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL))
- {
- fill_pathname_join(new_savefile_dir, new_savefile_dir, ".netplay",
- sizeof(new_savefile_dir));
- if (!path_is_directory(new_savefile_dir) &&
- !path_mkdir(new_savefile_dir))
- path_basedir(new_savefile_dir);
- }
- #endif
- if (system && !string_is_empty(system->library_name))
- {
- bool savefile_is_dir = path_is_directory(new_savefile_dir);
- bool savestate_is_dir = path_is_directory(new_savestate_dir);
- if (savefile_is_dir)
- strlcpy(runloop_st->name.savefile, new_savefile_dir,
- sizeof(runloop_st->name.savefile));
- else
- savefile_is_dir = path_is_directory(runloop_st->name.savefile);
- if (savestate_is_dir)
- {
- strlcpy(runloop_st->name.savestate, new_savestate_dir,
- sizeof(runloop_st->name.savestate));
- strlcpy(runloop_st->name.replay, new_savestate_dir,
- sizeof(runloop_st->name.replay));
- }
- else
- savestate_is_dir = path_is_directory(runloop_st->name.savestate);
- if (savefile_is_dir)
- {
- fill_pathname_dir(runloop_st->name.savefile,
- !string_is_empty(runloop_st->runtime_content_path_basename)
- ? runloop_st->runtime_content_path_basename
- : system->library_name,
- FILE_PATH_SRM_EXTENSION,
- sizeof(runloop_st->name.savefile));
- RARCH_LOG("[Overrides]: %s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVEFILE_TO),
- runloop_st->name.savefile);
- }
- if (savestate_is_dir)
- {
- fill_pathname_dir(runloop_st->name.savestate,
- !string_is_empty(runloop_st->runtime_content_path_basename)
- ? runloop_st->runtime_content_path_basename
- : system->library_name,
- FILE_PATH_STATE_EXTENSION,
- sizeof(runloop_st->name.savestate));
- fill_pathname_dir(runloop_st->name.replay,
- !string_is_empty(runloop_st->runtime_content_path_basename)
- ? runloop_st->runtime_content_path_basename
- : system->library_name,
- FILE_PATH_BSV_EXTENSION,
- sizeof(runloop_st->name.replay));
- RARCH_LOG("[Overrides]: %s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVESTATE_TO),
- runloop_st->name.savestate);
- }
- #ifdef HAVE_CHEATS
- if (path_is_directory(runloop_st->name.cheatfile))
- {
- fill_pathname_dir(runloop_st->name.cheatfile,
- !string_is_empty(runloop_st->runtime_content_path_basename)
- ? runloop_st->runtime_content_path_basename
- : system->library_name,
- FILE_PATH_CHT_EXTENSION,
- sizeof(runloop_st->name.cheatfile));
- RARCH_LOG("[Overrides]: %s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_CHEATFILE_TO),
- runloop_st->name.cheatfile);
- }
- #endif
- }
- dir_set(RARCH_DIR_CURRENT_SAVEFILE, new_savefile_dir);
- dir_set(RARCH_DIR_CURRENT_SAVESTATE, new_savestate_dir);
- }
- void runloop_path_deinit_subsystem(void)
- {
- runloop_state_t *runloop_st = &runloop_state;
- if (runloop_st->subsystem_fullpaths)
- string_list_free(runloop_st->subsystem_fullpaths);
- runloop_st->subsystem_fullpaths = NULL;
- }
- void runloop_path_set_special(char **argv, unsigned num_content)
- {
- unsigned i;
- char str[PATH_MAX_LENGTH];
- union string_list_elem_attr attr;
- bool is_dir = false;
- struct string_list subsystem_paths = {0};
- runloop_state_t *runloop_st = &runloop_state;
- const char *savestate_dir = runloop_st->savestate_dir;
- /* First content file is the significant one. */
- runloop_path_set_basename(argv[0]);
- string_list_initialize(&subsystem_paths);
- runloop_st->subsystem_fullpaths = string_list_new();
- attr.i = 0;
- for (i = 0; i < num_content; i++)
- {
- string_list_append(runloop_st->subsystem_fullpaths, argv[i], attr);
- strlcpy(str, argv[i], sizeof(str));
- path_remove_extension(str);
- string_list_append(&subsystem_paths, path_basename(str), attr);
- }
- str[0] = '\0';
- string_list_join_concat(str, sizeof(str), &subsystem_paths, " + ");
- string_list_deinitialize(&subsystem_paths);
- /* We defer SRAM path updates until we can resolve it.
- * It is more complicated for special content types. */
- is_dir = path_is_directory(savestate_dir);
- if (is_dir)
- {
- strlcpy(runloop_st->name.savestate, savestate_dir,
- sizeof(runloop_st->name.savestate)); /* TODO/FIXME - why are we setting this string here but then later overwriting it later with fil_pathname_dir? */
- strlcpy(runloop_st->name.replay, savestate_dir,
- sizeof(runloop_st->name.replay)); /* TODO/FIXME - as above */
- }
- else
- is_dir = path_is_directory(runloop_st->name.savestate);
- if (is_dir)
- {
- fill_pathname_dir(runloop_st->name.savestate,
- str,
- ".state",
- sizeof(runloop_st->name.savestate));
- fill_pathname_dir(runloop_st->name.replay,
- str,
- ".replay",
- sizeof(runloop_st->name.replay));
- RARCH_LOG("%s \"%s\".\n",
- msg_hash_to_str(MSG_REDIRECTING_SAVESTATE_TO),
- runloop_st->name.savestate);
- }
- }
|