Misc.cpp 157 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. #if WINDOWS_NEW
  6. using namespace concurrency;
  7. using namespace Platform::Collections;
  8. using namespace Windows::Foundation;
  9. using namespace Windows::Foundation::Collections;
  10. using namespace Windows::System;
  11. #elif MAC
  12. static PasteboardRef Pasteboard;
  13. #elif ANDROID || WEB
  14. static Str Clipboard;
  15. #endif
  16. const UID UIDZero(0, 0, 0, 0);
  17. /******************************************************************************/
  18. Int Compare(C CyclicUShort &a, C CyclicUShort &b) {return (a==b) ? 0 : (a<b) ? -1 : +1;}
  19. Int Compare(C CyclicUInt &a, C CyclicUInt &b) {return (a==b) ? 0 : (a<b) ? -1 : +1;}
  20. /******************************************************************************/
  21. Byte FltToByteScale (Flt x) {return Mid(RoundPos(x*256)-1, 0, 255);}
  22. Byte FltToByteScale2(Flt x) {return Mid(RoundPos(x*128)-1, 0, 255);}
  23. Flt ByteScaleToFlt (Byte x) {return (x+1)/256.0f;} // 0 -> 1/256, 127 -> 128/256 = 0.5, 255 -> 256/256 = 1.0
  24. Flt ByteScale2ToFlt(Byte x) {return (x+1)/128.0f;} // 0 -> 1/128, 127 -> 128/128 = 1.0, 255 -> 256/128 = 2.0
  25. Int ByteScaleRes(Int res, Byte byte_scale)
  26. {
  27. UInt byte_scale1=byte_scale+1;
  28. return Max(1, DivRound(res*byte_scale1, 256u));
  29. }
  30. VecI2 ByteScaleRes(C VecI2 &res, Byte byte_scale)
  31. {
  32. UInt byte_scale1=byte_scale+1;
  33. return VecI2(Max(1, DivRound(res.x*byte_scale1, 256u)),
  34. Max(1, DivRound(res.y*byte_scale1, 256u)));
  35. }
  36. Int ByteScale2Res(Int res, Byte byte_scale)
  37. {
  38. UInt byte_scale1=byte_scale+1;
  39. return Max(1, DivRound(res*byte_scale1, 128u));
  40. }
  41. VecI2 ByteScale2Res(C VecI2 &res, Byte byte_scale)
  42. {
  43. UInt byte_scale1=byte_scale+1;
  44. return VecI2(Max(1, DivRound(res.x*byte_scale1, 128u)),
  45. Max(1, DivRound(res.y*byte_scale1, 128u)));
  46. }
  47. /******************************************************************************
  48. Int ByteScaleResCeil(Int res, Byte byte_scale)
  49. {
  50. UInt byte_scale1=byte_scale+1;
  51. return DivCeil(res*byte_scale1, 256u); // because of 'DivCeil' we don't need Max(1, ..)
  52. }
  53. VecI2 ByteScaleResCeil(C VecI2 &res, Byte byte_scale)
  54. {
  55. UInt byte_scale1=byte_scale+1;
  56. return VecI2(DivCeil(res.x*byte_scale1, 256u), // because of 'DivCeil' we don't need Max(1, ..)
  57. DivCeil(res.y*byte_scale1, 256u)); // because of 'DivCeil' we don't need Max(1, ..)
  58. }
  59. Int ByteScale2ResCeil(Int res, Byte byte_scale)
  60. {
  61. UInt byte_scale1=byte_scale+1;
  62. return DivCeil(res*byte_scale1, 128u); // because of 'DivCeil' we don't need Max(1, ..)
  63. }
  64. VecI2 ByteScale2ResCeil(C VecI2 &res, Byte byte_scale)
  65. {
  66. UInt byte_scale1=byte_scale+1;
  67. return VecI2(DivCeil(res.x*byte_scale1, 128u), // because of 'DivCeil' we don't need Max(1, ..)
  68. DivCeil(res.y*byte_scale1, 128u)); // because of 'DivCeil' we don't need Max(1, ..)
  69. }
  70. /******************************************************************************/
  71. static void TestCyclic()
  72. {
  73. CyclicUInt a=0, b=0;
  74. DYNAMIC_ASSERT( (a>=b), "Cyclic");
  75. DYNAMIC_ASSERT(!(a> b), "Cyclic");
  76. DYNAMIC_ASSERT( (a<=b), "Cyclic");
  77. DYNAMIC_ASSERT(!(a< b), "Cyclic");
  78. DYNAMIC_ASSERT( (a==b), "Cyclic");
  79. DYNAMIC_ASSERT(!(a!=b), "Cyclic");
  80. DYNAMIC_ASSERT( (b>=a), "Cyclic");
  81. DYNAMIC_ASSERT(!(b> a), "Cyclic");
  82. DYNAMIC_ASSERT( (b<=a), "Cyclic");
  83. DYNAMIC_ASSERT(!(b< a), "Cyclic");
  84. DYNAMIC_ASSERT( (b==a), "Cyclic");
  85. DYNAMIC_ASSERT(!(b!=a), "Cyclic");
  86. a=UINT_MAX;
  87. DYNAMIC_ASSERT(!(a>=b), "Cyclic");
  88. DYNAMIC_ASSERT(!(a> b), "Cyclic");
  89. DYNAMIC_ASSERT( (a<=b), "Cyclic");
  90. DYNAMIC_ASSERT( (a< b), "Cyclic");
  91. DYNAMIC_ASSERT(!(a==b), "Cyclic");
  92. DYNAMIC_ASSERT( (a!=b), "Cyclic");
  93. DYNAMIC_ASSERT( (b>=a), "Cyclic");
  94. DYNAMIC_ASSERT( (b> a), "Cyclic");
  95. DYNAMIC_ASSERT(!(b<=a), "Cyclic");
  96. DYNAMIC_ASSERT(!(b< a), "Cyclic");
  97. DYNAMIC_ASSERT(!(b==a), "Cyclic");
  98. DYNAMIC_ASSERT( (b!=a), "Cyclic");
  99. a++;
  100. a++;
  101. DYNAMIC_ASSERT(a.v==1 , "Cyclic");
  102. DYNAMIC_ASSERT( (a>=b), "Cyclic");
  103. DYNAMIC_ASSERT( (a> b), "Cyclic");
  104. DYNAMIC_ASSERT(!(a<=b), "Cyclic");
  105. DYNAMIC_ASSERT(!(a< b), "Cyclic");
  106. DYNAMIC_ASSERT(!(a==b), "Cyclic");
  107. DYNAMIC_ASSERT( (a!=b), "Cyclic");
  108. DYNAMIC_ASSERT(!(b>=a), "Cyclic");
  109. DYNAMIC_ASSERT(!(b> a), "Cyclic");
  110. DYNAMIC_ASSERT( (b<=a), "Cyclic");
  111. DYNAMIC_ASSERT( (b< a), "Cyclic");
  112. DYNAMIC_ASSERT(!(b==a), "Cyclic");
  113. DYNAMIC_ASSERT( (b!=a), "Cyclic");
  114. }
  115. /******************************************************************************/
  116. SmoothValue ::SmoothValue (SMOOTH_VALUE_MODE mode) {Zero(T); T._mode=mode;}
  117. SmoothValue2::SmoothValue2(SMOOTH_VALUE_MODE mode) {Zero(T); T._mode=mode;}
  118. void SmoothValue ::mode(SMOOTH_VALUE_MODE mode) {if(T._mode!=mode){T._mode=mode; init(_history[0]);}}
  119. void SmoothValue2::mode(SMOOTH_VALUE_MODE mode) {if(T._mode!=mode){T._mode=mode; init(_history[0]);}}
  120. void SmoothValue ::init( Flt start_value) {REPAO(_history)=start_value;}
  121. void SmoothValue2::init(C Vec2 &start_value) {REPAO(_history)=start_value;}
  122. Flt SmoothValue::update(Flt current_value)
  123. {
  124. Flt value;
  125. switch(_mode)
  126. {
  127. default: return _history[0]=current_value;
  128. #if 1
  129. case SV_WEIGHT2: _history[_pos]=current_value; value=0; REP(2)value+=BlendSmoothCube(i/2.0f)*_history[ (_pos-i)&1]; (++_pos)&=1; return value/BlendSmoothCubeSumHalf(2);
  130. case SV_WEIGHT3: _history[_pos]=current_value; value=0; _pos+=3; REP(3)value+=BlendSmoothCube(i/3.0f)*_history[Unsigned(_pos-i)%3]; (++_pos)%=3; return value/BlendSmoothCubeSumHalf(3); // have to increase '_pos' before the loop to avoid negative numbers for %
  131. case SV_WEIGHT4: _history[_pos]=current_value; value=0; REP(4)value+=BlendSmoothCube(i/4.0f)*_history[ (_pos-i)&3]; (++_pos)&=3; return value/BlendSmoothCubeSumHalf(4);
  132. case SV_WEIGHT8: _history[_pos]=current_value; value=0; REP(8)value+=BlendSmoothCube(i/8.0f)*_history[ (_pos-i)&7]; (++_pos)&=7; return value/BlendSmoothCubeSumHalf(8);
  133. #else
  134. Flt power;
  135. case SV_WEIGHT2: _history[_pos]=current_value; value=power=0; REP(2){Flt p=BlendSmoothCube(i/2.0f); value+=p*_history[ (_pos-i)&1]; power+=p;} (++_pos)&=1; return value/power;
  136. case SV_WEIGHT3: _history[_pos]=current_value; value=power=0; _pos+=3; REP(3){Flt p=BlendSmoothCube(i/3.0f); value+=p*_history[Unsigned(_pos-i)%3]; power+=p;} (++_pos)%=3; return value/power; // have to increase '_pos' before the loop to avoid negative numbers for %
  137. case SV_WEIGHT4: _history[_pos]=current_value; value=power=0; REP(4){Flt p=BlendSmoothCube(i/4.0f); value+=p*_history[ (_pos-i)&3]; power+=p;} (++_pos)&=3; return value/power;
  138. case SV_WEIGHT8: _history[_pos]=current_value; value=power=0; REP(8){Flt p=BlendSmoothCube(i/8.0f); value+=p*_history[ (_pos-i)&7]; power+=p;} (++_pos)&=7; return value/power;
  139. #endif
  140. case SV_AVERAGE2: _history[_pos]=current_value; value=0; REP(2)value+=_history[i]; (++_pos)&=1; return value/2;
  141. case SV_AVERAGE3: _history[_pos]=current_value; value=0; REP(3)value+=_history[i]; (++_pos)%=3; return value/3;
  142. case SV_AVERAGE4: _history[_pos]=current_value; value=0; REP(4)value+=_history[i]; (++_pos)&=3; return value/4;
  143. case SV_AVERAGE8: _history[_pos]=current_value; value=0; REP(8)value+=_history[i]; (++_pos)&=7; return value/8;
  144. }
  145. }
  146. Vec2 SmoothValue2::update(C Vec2 &current_value)
  147. {
  148. Vec2 value;
  149. switch(_mode)
  150. {
  151. default: return _history[0]=current_value;
  152. #if 1
  153. case SV_WEIGHT2: _history[_pos]=current_value; value=0; REP(2)value+=BlendSmoothCube(i/2.0f)*_history[ (_pos-i)&1]; (++_pos)&=1; return value/BlendSmoothCubeSumHalf(2);
  154. case SV_WEIGHT3: _history[_pos]=current_value; value=0; _pos+=3; REP(3)value+=BlendSmoothCube(i/3.0f)*_history[Unsigned(_pos-i)%3]; (++_pos)%=3; return value/BlendSmoothCubeSumHalf(3); // have to increase '_pos' before the loop to avoid negative numbers for %
  155. case SV_WEIGHT4: _history[_pos]=current_value; value=0; REP(4)value+=BlendSmoothCube(i/4.0f)*_history[ (_pos-i)&3]; (++_pos)&=3; return value/BlendSmoothCubeSumHalf(4);
  156. case SV_WEIGHT8: _history[_pos]=current_value; value=0; REP(8)value+=BlendSmoothCube(i/8.0f)*_history[ (_pos-i)&7]; (++_pos)&=7; return value/BlendSmoothCubeSumHalf(8);
  157. #else
  158. Flt power;
  159. case SV_WEIGHT2: _history[_pos]=current_value; value=power=0; REP(2){Flt p=BlendSmoothCube(i/2.0f); value+=p*_history[ (_pos-i)&1]; power+=p;} (++_pos)&=1; return value/power;
  160. case SV_WEIGHT3: _history[_pos]=current_value; value=power=0; _pos+=3; REP(3){Flt p=BlendSmoothCube(i/3.0f); value+=p*_history[Unsigned(_pos-i)%3]; power+=p;} (++_pos)%=3; return value/power; // have to increase '_pos' before the loop to avoid negative numbers for %
  161. case SV_WEIGHT4: _history[_pos]=current_value; value=power=0; REP(4){Flt p=BlendSmoothCube(i/4.0f); value+=p*_history[ (_pos-i)&3]; power+=p;} (++_pos)&=3; return value/power;
  162. case SV_WEIGHT8: _history[_pos]=current_value; value=power=0; REP(8){Flt p=BlendSmoothCube(i/8.0f); value+=p*_history[ (_pos-i)&7]; power+=p;} (++_pos)&=7; return value/power;
  163. #endif
  164. case SV_AVERAGE2: _history[_pos]=current_value; value=0; REP(2)value+=_history[i]; (++_pos)&=1; return value/2;
  165. case SV_AVERAGE3: _history[_pos]=current_value; value=0; REP(3)value+=_history[i]; (++_pos)%=3; return value/3;
  166. case SV_AVERAGE4: _history[_pos]=current_value; value=0; REP(4)value+=_history[i]; (++_pos)&=3; return value/4;
  167. case SV_AVERAGE8: _history[_pos]=current_value; value=0; REP(8)value+=_history[i]; (++_pos)&=7; return value/8;
  168. }
  169. }
  170. /******************************************************************************/
  171. SmoothValueTime ::SmoothValueTime (Flt history_time) {Zero(T); historyTime(history_time);}
  172. SmoothValueTime2::SmoothValueTime2(Flt history_time) {Zero(T); historyTime(history_time);}
  173. SmoothValueTime& SmoothValueTime::historyTime(Flt time)
  174. {
  175. Flt history_time=Max(time, 0.0f);
  176. T._step_time =Max(history_time/Elms(_history), EPS); // to avoid div by zero
  177. return T;
  178. }
  179. SmoothValueTime2& SmoothValueTime2::historyTime(Flt time)
  180. {
  181. Flt history_time=Max(time, 0.0f);
  182. T._step_time =Max(history_time/Elms(_history), EPS); // to avoid div by zero
  183. return T;
  184. }
  185. Flt SmoothValueTime::update(Flt current_value, Flt dt)
  186. {
  187. if(dt>0)
  188. {
  189. Flt left=_step_time-_time, process=Min(left, dt);
  190. _history[_pos]+=current_value*process; // add to current step
  191. _time +=dt;
  192. MIN(_time_total +=dt, (Elms(_history)-1)*_step_time); // -1 because "_history[_pos]" (current) and "_history[(_pos+1)%elms]" (oldest) are partial
  193. if(Int steps=Trunc(_time/_step_time))
  194. {
  195. _time-=steps*_step_time;
  196. if(steps>1) // following code section is needed only if there are more than 1 steps
  197. {
  198. steps--; // this step was already processed above
  199. // process full steps
  200. MIN(steps, Elms(_history)-1); // don't process more than we can actually handle, limit to one less because we're adding "last step part" on the bottom
  201. Flt val_step_time=current_value*_step_time;
  202. REP(steps)
  203. {
  204. (++_pos)&=Elms(_history)-1; ASSERT(IS_POW_2(ELMS(_history)));
  205. _history[_pos]=val_step_time;
  206. }
  207. }
  208. // process last step part
  209. (++_pos)&=Elms(_history)-1; ASSERT(IS_POW_2(ELMS(_history)));
  210. _history[_pos]=current_value*_time;
  211. }
  212. }
  213. if(_time_total)
  214. {
  215. Int oldest=(_pos+1)&(Elms(_history)-1); ASSERT(IS_POW_2(ELMS(_history)));
  216. Flt val=0; REPA(_history)val+=_history[i]; val-=_history[oldest]*(_time/_step_time); // '_history[oldest]' was added as full, however we need it to be "_history[oldest]*(1-_time/_step_time)" so just subtract "_history[oldest]*(_time/_step_time)"
  217. return val/_time_total;
  218. }
  219. return 0;
  220. }
  221. Vec2 SmoothValueTime2::update(C Vec2 &current_value, Flt dt)
  222. {
  223. if(dt>0)
  224. {
  225. Flt left=_step_time-_time, process=Min(left, dt);
  226. _history[_pos]+=current_value*process; // add to current step
  227. _time +=dt;
  228. MIN(_time_total +=dt, (Elms(_history)-1)*_step_time); // -1 because "_history[_pos]" (current) and "_history[(_pos+1)%elms]" (oldest) are partial
  229. if(Int steps=Trunc(_time/_step_time))
  230. {
  231. _time-=steps*_step_time;
  232. if(steps>1) // following code section is needed only if there are more than 1 steps
  233. {
  234. steps--; // this step was already processed above
  235. // process full steps
  236. MIN(steps, Elms(_history)-1); // don't process more than we can actually handle, limit to one less because we're adding "last step part" on the bottom
  237. Vec2 val_step_time=current_value*_step_time;
  238. REP(steps)
  239. {
  240. (++_pos)&=Elms(_history)-1; ASSERT(IS_POW_2(ELMS(_history)));
  241. _history[_pos]=val_step_time;
  242. }
  243. }
  244. // process last step part
  245. (++_pos)&=Elms(_history)-1; ASSERT(IS_POW_2(ELMS(_history)));
  246. _history[_pos]=current_value*_time;
  247. }
  248. }
  249. if(_time_total)
  250. {
  251. Int oldest=(_pos+1)&(Elms(_history)-1); ASSERT(IS_POW_2(ELMS(_history)));
  252. Vec2 val=0; REPA(_history)val+=_history[i]; val-=_history[oldest]*(_time/_step_time); // '_history[oldest]' was added as full, however we need it to be "_history[oldest]*(1-_time/_step_time)" so just subtract "_history[oldest]*(_time/_step_time)"
  253. return val/_time_total;
  254. }
  255. return 0;
  256. }
  257. /******************************************************************************
  258. flt delta=target-value;
  259. flt accel_time;
  260. flt vel_mid=velocity+accel*accel_time; // velocity at middle point (after acceleration and before braking)
  261. flt brake_time=vel_mid/accel;
  262. flt brake_dist=accel/-2*brake_time*brake_time + vel_mid*brake_time;
  263. flt remaining_dist=delta - accel/2*accel_time*accel_time - velocity*accel_time;
  264. brake_dist=remaining_dist;
  265. accel/-2*brake_time*brake_time + vel_mid*brake_time = delta - accel/2*accel_time*accel_time - velocity*accel_time;
  266. accel/-2*Sqr((velocity+accel*accel_time)/accel) + (velocity+accel*accel_time)*(velocity+accel*accel_time)/accel = target-value - accel/2*accel_time*accel_time - velocity*accel_time;
  267. accel/-2*Sqr(velocity/accel+accel_time) + (velocity*velocity + accel*accel*accel_time*accel_time + 2*velocity*accel*accel_time)/accel + value-target + accel/2*accel_time*accel_time + velocity*accel_time = 0;
  268. accel/-2*(velocity*velocity/accel/accel + accel_time*accel_time + 2*velocity/accel*accel_time) + velocity*velocity/accel + accel*accel_time*accel_time + 2*velocity*accel_time + value-target + accel/2*accel_time*accel_time + velocity*accel_time = 0;
  269. velocity*velocity/accel/-2 + accel_time*accel_time*accel/-2 - velocity*accel_time + velocity*velocity/accel + accel*accel_time*accel_time + 2*velocity*accel_time + value-target + accel/2*accel_time*accel_time + velocity*accel_time = 0;
  270. accel_time*accel_time*accel/-2 + accel*accel_time*accel_time + accel/2*accel_time*accel_time - velocity*accel_time + 2*velocity*accel_time + velocity*accel_time + velocity*velocity/accel/-2 + velocity*velocity/accel + value-target = 0;
  271. accel_time*accel_time*(accel/-2 + accel + accel/2) + accel_time*(-velocity + 2*velocity + velocity) + velocity*velocity/accel*(1/-2 + 1) + value-target = 0;
  272. accel_time*accel_time*accel + accel_time*2*velocity + velocity*velocity/accel*0.5 + value-target = 0; */
  273. /******************************************************************************/
  274. void SmoothValueSettings::reset(Flt max_accel) {time_delta=Time.d(); T.max_accel=max_accel; max_velocity=FLT_MAX;}
  275. /******************************************************************************/
  276. void SmoothValueAccel::update(Flt target, C SmoothValueSettings &settings)
  277. {
  278. if(value!=target || velocity)
  279. if(Flt t=settings.time_delta)
  280. {
  281. Flt accel;
  282. if(target>value)accel= settings.max_accel;else
  283. if(target<value)accel=-settings.max_accel;else
  284. if(velocity<0 )accel= settings.max_accel;else
  285. accel=-settings.max_accel;
  286. Flt a=accel, b=2*velocity, c=velocity*velocity/accel*0.5f + value-target, d=Sqr(b)-4*a*c;
  287. //if(d>=0)
  288. {
  289. //Flt accel_time=(-b+Sign(a)*SqrtFast(d))/(2*a);
  290. Flt accel_time=-b; if(d>0)accel_time+=Sign(a)*SqrtFast(d); accel_time/=2*a;
  291. if( accel_time>0) // accelerate
  292. {
  293. MIN(accel_time, t);
  294. // value += accel/2*t*t + velocity*t
  295. // velocity += accel *t
  296. Flt old_velocity=velocity;
  297. velocity=Mid(old_velocity+accel*accel_time, -settings.max_velocity, settings.max_velocity);
  298. value+=Avg(old_velocity, velocity)*accel_time;
  299. /*#if DEBUG
  300. Flt brake_time=velocity/accel;
  301. Flt brake_dist=accel/-2*brake_time*brake_time + velocity*brake_time;
  302. Flt remaining_dist=target-value;
  303. if(accel_time>EPS)D.text(0, 0, S+"accel");
  304. #endif*/
  305. t-=accel_time;
  306. }
  307. if(t>0) // brake
  308. {
  309. Flt old_velocity=velocity;
  310. velocity=old_velocity-accel*t;
  311. if(Sign(old_velocity)!=Sign(velocity))
  312. {
  313. velocity=0;
  314. value=target;
  315. }else value+=Avg(old_velocity, velocity)*t;
  316. /*#if DEBUG
  317. D.text(0, -0.1, S+"slow");
  318. #endif*/
  319. }
  320. }
  321. }
  322. }
  323. void SmoothValueAccel2::update(C Vec2 &target, C SmoothValueSettings &settings)
  324. {
  325. if(value!=target || velocity.any())
  326. if(settings.time_delta)
  327. {
  328. Vec2 accel;
  329. FREP(2)
  330. if(target .c[i]>value.c[i])accel.c[i]= settings.max_accel;else
  331. if(target .c[i]<value.c[i])accel.c[i]=-settings.max_accel;else
  332. if(velocity.c[i]<0 )accel.c[i]= settings.max_accel;else
  333. accel.c[i]=-settings.max_accel;
  334. Vec2 a=accel, b=2*velocity, c=velocity*velocity/accel*0.5f + value-target, d=Sqr(b)-4*a*c, accel_time=-b;
  335. FREP(2)if(d.c[i]>0)accel_time.c[i]+=Sign(a.c[i])*SqrtFast(d.c[i]);
  336. accel_time/=2*a;
  337. /*
  338. Flt accel_time=(-b+Sign(a)*SqrtFast(d))/(2*a);
  339. Flt brake_time=velocity/accel + accel_time
  340. Flt total_time=accel_time+brake_time;
  341. Flt total_time=accel_time*2 + velocity/accel
  342. total_time1=total_time2
  343. accel_time*2 + velocity/accel =
  344. (-2*velocity+Sqrt(Sqr(b)-4*a*c))/(2*a)*2 + velocity/accel =
  345. (-2*velocity+Sqrt(4*velocity*velocity-4*accel*(velocity*velocity/accel/2 + value-target)))/accel + velocity/accel =
  346. (-2*velocity+Sqrt(4*velocity*velocity - 2*velocity*velocity -4*accel*(value-target)))/accel + velocity/accel =
  347. (-2*velocity + Sqrt(2*velocity*velocity -4*accel*(value-target)) + velocity)/accel =
  348. (Sqrt(2*velocity*velocity-4*accel*(value-target))-velocity)/accel =
  349. (Sqrt(2*velocity_x*velocity_x-4*accel_x*(value_x-target_x))-velocity_x)/accel_x = (Sqrt(2*velocity_y*velocity_y-4*accel_y*(value_y-target_y))-velocity_y)/accel_y
  350. (Sqrt(2*velocity_x*velocity_x-4*accel_x*(value_x-target_x))-velocity_x)*accel_y = (Sqrt(2*velocity_y*velocity_y-4*accel_y*(value_y-target_y))-velocity_y)*accel_x
  351. accel_x*accel_x + accel_y*accel_y = max_accel*max_accel
  352. accel_y*accel_y = max_accel*max_accel - accel_x*accel_x
  353. accel_y = Sqrt(max_accel*max_accel - accel_x*accel_x)
  354. (Sqrt(2*velocity_x*velocity_x-4*accel_x*(value_x-target_x))-velocity_x)*Sqrt(max_accel*max_accel - accel_x*accel_x) = (Sqrt(2*velocity_y*velocity_y-4*Sqrt(max_accel*max_accel - accel_x*accel_x)*(value_y-target_y))-velocity_y)*accel_x
  355. */
  356. Vec2 brake_time=velocity/accel; // (velocity+accel*Max(0, accel_time))/accel
  357. FREP(2)if(accel_time.c[i]>0)brake_time.c[i]+=accel_time.c[i];
  358. Vec2 total_time=accel_time+brake_time;
  359. accel*=Sqr(total_time); // TODO: this is an approximation
  360. accel.setLength(settings.max_accel);
  361. //Flt x=(Sqrt(2*velocity.x*velocity.x-4*accel.x*(value.x-target.x))-velocity.x)/accel.x,
  362. // y=(Sqrt(2*velocity.y*velocity.y-4*accel.y*(value.y-target.y))-velocity.y)/accel.y;
  363. a=accel; c=velocity*velocity/accel*0.5f + value-target; d=Sqr(b)-4*a*c; accel_time=-b;
  364. FREP(2)if(d.c[i]>0)accel_time.c[i]+=Sign(a.c[i])*SqrtFast(d.c[i]);
  365. accel_time/=2*a;
  366. //brake_time=velocity/accel; // (velocity+accel*Max(0, accel_time))/accel
  367. //FREP(2)if(accel_time.c[i]>0)brake_time.c[i]+=accel_time.c[i];
  368. //total_time=accel_time+brake_time;
  369. FREP(2)if(Flt ac=accel.c[i])
  370. {
  371. Flt t=settings.time_delta, act=accel_time.c[i], &velocity=T.velocity.c[i], &value=T.value.c[i];
  372. if(act>0) // accelerate
  373. {
  374. MIN(act, t);
  375. Flt old_velocity=velocity;
  376. velocity=Mid(old_velocity+ac*act, -settings.max_velocity, settings.max_velocity);
  377. value+=Avg(old_velocity, velocity)*act;
  378. t-=act;
  379. }
  380. if(t>0) // brake
  381. {
  382. Flt old_velocity=velocity;
  383. velocity=old_velocity-ac*t;
  384. if(Sign(old_velocity)!=Sign(velocity))
  385. {
  386. velocity=0;
  387. value=target.c[i];
  388. }else value+=Avg(old_velocity, velocity)*t;
  389. }
  390. }
  391. }
  392. }
  393. void SmoothValueAccel3::update(C Vec &target, C SmoothValueSettings &settings)
  394. {
  395. if(value!=target || velocity.any())
  396. if(settings.time_delta)
  397. {
  398. Vec accel;
  399. FREP(3)
  400. if(target .c[i]>value.c[i])accel.c[i]= settings.max_accel;else
  401. if(target .c[i]<value.c[i])accel.c[i]=-settings.max_accel;else
  402. if(velocity.c[i]<0 )accel.c[i]= settings.max_accel;else
  403. accel.c[i]=-settings.max_accel;
  404. Vec a=accel, b=2*velocity, c=velocity*velocity/accel*0.5f + value-target, d=Sqr(b)-4*a*c, accel_time=-b;
  405. FREP(3)if(d.c[i]>0)accel_time.c[i]+=Sign(a.c[i])*SqrtFast(d.c[i]);
  406. accel_time/=2*a;
  407. Vec brake_time=velocity/accel; // (velocity+accel*Max(0, accel_time))/accel
  408. FREP(3)if(accel_time.c[i]>0)brake_time.c[i]+=accel_time.c[i];
  409. Vec total_time=accel_time+brake_time;
  410. accel*=Sqr(total_time); // TODO: this is an approximation
  411. accel.setLength(settings.max_accel);
  412. a=accel; c=velocity*velocity/accel*0.5f + value-target; d=Sqr(b)-4*a*c; accel_time=-b;
  413. FREP(3)if(d.c[i]>0)accel_time.c[i]+=Sign(a.c[i])*SqrtFast(d.c[i]);
  414. accel_time/=2*a;
  415. FREP(3)if(Flt ac=accel.c[i])
  416. {
  417. Flt t=settings.time_delta, act=accel_time.c[i], &velocity=T.velocity.c[i], &value=T.value.c[i];
  418. if(act>0) // accelerate
  419. {
  420. MIN(act, t);
  421. Flt old_velocity=velocity;
  422. velocity=Mid(old_velocity+ac*act, -settings.max_velocity, settings.max_velocity);
  423. value+=Avg(old_velocity, velocity)*act;
  424. t-=act;
  425. }
  426. if(t>0) // brake
  427. {
  428. Flt old_velocity=velocity;
  429. velocity=old_velocity-ac*t;
  430. if(Sign(old_velocity)!=Sign(velocity))
  431. {
  432. velocity=0;
  433. value=target.c[i];
  434. }else value+=Avg(old_velocity, velocity)*t;
  435. }
  436. }
  437. }
  438. }
  439. /******************************************************************************/
  440. #if LINUX || ANDROID
  441. static struct DevRandom
  442. {
  443. Bool initialized; // additional helper member used in case 'DevRandom' gets called before its constructor or after its destructor, this is assumed to be false at app startup (because it belongs to static global 'DR' variable)
  444. int fd;
  445. static int getFD()
  446. {
  447. int fd=open("/dev/urandom", O_RDONLY|O_NONBLOCK); // 'urandom' is lower precision but non-blocking
  448. //if(fd<0)fd=open("/dev/random" , O_RDONLY|O_NONBLOCK); this often returns -1 when reading, so don't use
  449. return fd;
  450. }
  451. Bool set(Ptr data, Int size) // assumes that "data!=null"
  452. {
  453. Int r;
  454. if(initialized)r=read(fd, data, size);else
  455. { // if object was not yet initialized or was already destroyed
  456. r=0;
  457. int temp_fd =getFD(); // use temporary file descriptor
  458. if( temp_fd>=0){r=read(temp_fd, data, size); close(temp_fd);} // close it afterwards
  459. }
  460. return r==size;
  461. }
  462. DevRandom() {fd=getFD(); initialized=(fd>=0);}
  463. ~DevRandom() {if(initialized){initialized=false ; close(fd); fd=-1;}}
  464. }DR;
  465. #endif
  466. UID& UID::randomize()
  467. {
  468. #if WINDOWS_OLD
  469. #if 1 // 9x slower, more secure (MAC not included), totally random
  470. UuidCreate(&guid());
  471. #else // 9x faster, less secure (MAC included), ID's are sequential (they increase by 1), better don't use this
  472. UuidCreateSequential(&guid());
  473. #endif
  474. return T;
  475. #elif WINDOWS_NEW
  476. CoCreateGuid(&guid());
  477. return T;
  478. #elif APPLE
  479. if(CFUUIDRef uuid=CFUUIDCreate(kCFAllocatorDefault))
  480. {
  481. ASSERT(SIZE(CFUUIDBytes)==SIZE(T));
  482. CFUUIDBytes uuid_bytes=CFUUIDGetUUIDBytes(uuid);
  483. T=(UID&)uuid_bytes;
  484. CFRelease(uuid);
  485. return T;
  486. }
  487. #elif LINUX || ANDROID
  488. if(DR.set(this, SIZE(T)))return T;
  489. #elif WEB
  490. if(EM_ASM_INT(var crypto=window.crypto || window.msCrypto; return typeof(crypto)!=='undefined' && typeof(crypto.getRandomValues)!=='undefined'))
  491. {
  492. EM_ASM
  493. ({
  494. var rnd=new Uint32Array(4); (window.crypto || window.msCrypto).getRandomValues(rnd);
  495. for(var i=0; i<4; i++)setValue($0+i*4, rnd[i], 'i32');
  496. }, i);
  497. return T;
  498. }
  499. #endif
  500. #if X64
  501. REPAO(T.l)=Random.l();
  502. #else
  503. REPAO(T.i)=Random();
  504. #endif
  505. return T;
  506. }
  507. UID& UID::randomizeValid()
  508. {
  509. for(;;)
  510. {
  511. randomize();
  512. if(valid())return T;
  513. }
  514. }
  515. StrO UID:: asHex( )C {return TextHexMem( this, SIZE(T), false);}
  516. Bool UID::fromHex(C Str &text) {return TextHexMem(text, this, SIZE(T) );}
  517. #if 0
  518. StrO UID::asHexRev()C
  519. {
  520. Char8 temp[256], out[33];
  521. Set(out, TextHex(l[1], temp, 16));
  522. Append(out, TextHex(l[0], temp, 16));
  523. return out;
  524. }
  525. Bool UID::fromHexRev(C Str &text)
  526. {
  527. Bool ok =(text.length()==32); // 32 (0..F) 4-bit digs = 128-bit total
  528. Byte *dest=b+15; // start from last byte because first characters represent most significant values
  529. Int i =0;
  530. for(Int src=0, process=Min(SIZEI(T), (text.length()+1)/2); i<process; ) // "(length+1)/2" because we want to process all existing characters so that "5?" gets converted to 0x50 even though '?' is invalid
  531. {
  532. Int hi=CharInt(text[src++]); if(!InRange(hi, 16)){ ok=false; break;} hi<<=4;
  533. Int lo=CharInt(text[src++]); if(!InRange(lo, 16)){*dest--=hi; i++; ok=false; break;} // set 'dest' from 'hi' only and increase 'i' to mark it as processed
  534. *dest--=(lo|hi); i++;
  535. }
  536. for(; i<SIZE(T); i++)*dest--=0; // clear unprocessed with zeros
  537. return ok;
  538. }
  539. #endif
  540. StrO UID::asCString()C
  541. {
  542. return StrO()+"UID("+i[0]+", "+i[1]+", "+i[2]+", "+i[3]+')';
  543. }
  544. Bool UID::fromCString(C Str &text)
  545. {
  546. if(CChar *t=_SkipWhiteChars(text))
  547. {
  548. if(Equal(t[0], 'U') && Equal(t[1], 'I') && Equal(t[2], 'D'))t=_SkipWhiteChars(t+3); // skip "UID"
  549. if(t && t[0]=='(')
  550. {
  551. t++;
  552. CalcValue value;
  553. Int commas=0; for(CChar *temp=t; ; ){Char c=*temp++; if(!c)break; if(c==',')commas++;}
  554. if( commas==16-1) // bytes
  555. {
  556. FREPA(b)
  557. {
  558. t=TextValue(t, value); if(i<commas)t=_SkipChar(TextPos(t, ',')); if(!value.type)goto error; b[i]=value.asUInt();
  559. }
  560. }else
  561. if(commas==4-1) // uints
  562. {
  563. FREPA(T.i)
  564. {
  565. t=TextValue(t, value); if(i<commas)t=_SkipChar(TextPos(t, ',')); if(!value.type)goto error; T.i[i]=value.asUInt();
  566. }
  567. }else
  568. if(commas==2-1) // ulongs
  569. {
  570. FREPA(l)
  571. {
  572. t=TextValue(t, value, false); if(i<commas)t=_SkipChar(TextPos(t, ',')); if(!value.type)goto error; l[i]=value.asULong(); // have to disable reals, because otherwise some ULong's could get converted and lose precise value
  573. }
  574. }else goto error;
  575. if(t=_SkipWhiteChars(t))if(t[0]==')')return true;
  576. return false; // leave contents on fail
  577. }
  578. }
  579. error:
  580. zero(); return false;
  581. }
  582. StrO UID:: asFileName( )C {return _EncodeFileName( T);}
  583. Bool UID::fromFileName(C Str &text) {return DecodeFileName(text, T);}
  584. StrO UID::asCanonical()C
  585. {
  586. StrO s; s.reserve(32+4);
  587. for(Int i= 4-1; i>= 0; i--){Byte b=T.b[i]; s+=Digits16[b>>4]; s+=Digits16[b&15];} s+='-';
  588. for(Int i= 6-1; i>= 4; i--){Byte b=T.b[i]; s+=Digits16[b>>4]; s+=Digits16[b&15];} s+='-';
  589. for(Int i= 8-1; i>= 6; i--){Byte b=T.b[i]; s+=Digits16[b>>4]; s+=Digits16[b&15];} s+='-';
  590. for(Int i= 8 ; i< 10; i++){Byte b=T.b[i]; s+=Digits16[b>>4]; s+=Digits16[b&15];} s+='-';
  591. for(Int i=10 ; i< 16; i++){Byte b=T.b[i]; s+=Digits16[b>>4]; s+=Digits16[b&15];}
  592. return s;
  593. }
  594. Bool UID::fromCanonical(C Str &text)
  595. {
  596. if(text.length()==32+4)
  597. {
  598. CChar *t=text();
  599. for(Int i= 4-1; i>= 0; i--){Int hi=CharInt(*t++); Int lo=CharInt(*t++); b[i]=lo|(hi<<4); if(!InRange(hi, 16) || !InRange(lo, 16))goto error;} if(*t++!='-')goto error;
  600. for(Int i= 6-1; i>= 4; i--){Int hi=CharInt(*t++); Int lo=CharInt(*t++); b[i]=lo|(hi<<4); if(!InRange(hi, 16) || !InRange(lo, 16))goto error;} if(*t++!='-')goto error;
  601. for(Int i= 8-1; i>= 6; i--){Int hi=CharInt(*t++); Int lo=CharInt(*t++); b[i]=lo|(hi<<4); if(!InRange(hi, 16) || !InRange(lo, 16))goto error;} if(*t++!='-')goto error;
  602. for(Int i= 8 ; i< 10; i++){Int hi=CharInt(*t++); Int lo=CharInt(*t++); b[i]=lo|(hi<<4); if(!InRange(hi, 16) || !InRange(lo, 16))goto error;} if(*t++!='-')goto error;
  603. for(Int i=10 ; i< 16; i++){Int hi=CharInt(*t++); Int lo=CharInt(*t++); b[i]=lo|(hi<<4); if(!InRange(hi, 16) || !InRange(lo, 16))goto error;}
  604. return true;
  605. }
  606. error:
  607. zero(); return false;
  608. }
  609. Bool UID::fromText(C Str &text)
  610. {
  611. Str t=text;
  612. Int end=TextPosI(t, ')' ); if(end>=0)t.clip(end+1); // remove everything after ')' , like comments UID(..) /* this is ...
  613. end=TextPosI(t, '"' ); if(end>=0)t.clip(end ); // remove everything until '"' , like comments "bvnbasd" /* this is ...
  614. end=TextPosI(t, '\n'); if(end>=0)t.clip(end ); // remove everything until '\n', this is needed in case there are multiple ID's per line (for example used in PARAM_ID_ARRAY)
  615. t.removeOuterWhiteChars(); if(t.first()=='"')t.remove(0); // remove first quotation (ending is done above)
  616. if( fromHex(t ))return true; // 32 char
  617. if( fromCString(t ))return true;
  618. if(DecodeFileName(t, T))return true; // 24 char
  619. zero(); return false;
  620. }
  621. UID& UID::operator+=(Int i)
  622. {
  623. ULong temp=T.l[0]; T.l[0]+=i;
  624. if(i>0){if(T.l[0]<temp)T.l[1]++;}else
  625. if(i<0){if(T.l[0]>temp)T.l[1]--;}
  626. return T;
  627. }
  628. UID& UID::operator-=(Int i)
  629. {
  630. ULong temp=T.l[0]; T.l[0]-=i;
  631. if(i<0){if(T.l[0]<temp)T.l[1]++;}else
  632. if(i>0){if(T.l[0]>temp)T.l[1]--;}
  633. return T;
  634. }
  635. UID& UID::operator+=(UInt i) {ULong temp=T.l[0]; T.l[0]+=i; if(T.l[0]<temp)T.l[1]++; return T;}
  636. UID& UID::operator-=(UInt i) {ULong temp=T.l[0]; T.l[0]-=i; if(T.l[0]>temp)T.l[1]--; return T;}
  637. /******************************************************************************/
  638. // '_returned' could be a Memc<VecI2> holding ranges instead of single values, which would result in smaller memory usage, however also in smaller performance, because returning values in "Return" method would require iterating all ranges
  639. IDGenerator::~IDGenerator()
  640. {
  641. _created=0; // set '_created' to zero, so when calling 'Return' after destructor was called, it will get ignored (this can happen if calling 'Return' from a destructor of a secondary object, after destructor of 'IDGenerator' was already called)
  642. }
  643. IDGenerator::IDGenerator()
  644. {
  645. _created=0;
  646. }
  647. UInt IDGenerator::New() // create new ID
  648. {
  649. if(_returned.elms())return _returned.pop(); // if we have an ID that was created and later returned, then give it
  650. return _created++; // create new one
  651. }
  652. void IDGenerator::Return(UInt id) // return ID so it can be re-used later
  653. {
  654. if(id<_created) // if this ID fits in range of what was created
  655. {
  656. if(_returned.elms()==_created-1) // if returned all that were created
  657. { // reset counter
  658. _created=0;
  659. _returned.clear();
  660. }else
  661. if(id==_created-1)_created--; // if this is the one that was created most recently
  662. else _returned.add(id); // add to the list of returned ID's
  663. }
  664. }
  665. /******************************************************************************/
  666. #if DESKTOP
  667. static Char LogFile[MAX_LONG_PATH]={'l', 'o', 'g', '.', 't', 'x', 't', '\0'}; // use Char array to allow working even after app is being destroyed (destructors called)
  668. #else
  669. static Char LogFile[MAX_LONG_PATH];
  670. #endif
  671. static Bool LogThreadID, LogDate, LogTime, LogCurTime, LogConsoleOn;
  672. void LogConsole(Bool on)
  673. {
  674. #if WINDOWS_OLD
  675. if(LogConsoleOn!=on)
  676. {
  677. Bool active=App.active();
  678. if(!on)LogConsoleOn=!( FreeConsole()!=0);else
  679. if( LogConsoleOn= (AllocConsole()!=0))
  680. {
  681. SetConsoleCtrlHandler(null, true); // this disables closing the console via Ctrl+C
  682. if(HWND hwnd=GetConsoleWindow())if(HMENU menu=GetSystemMenu(hwnd, false))DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND); // this disables the Close button on the console window because on Windows OS once it's clicked, then entire App gets terminated immediately
  683. FILE *f;
  684. f=null; freopen_s(&f, "CONIN$" , "rb", stdin );
  685. f=null; freopen_s(&f, "CONOUT$", "wb", stdout);
  686. f=null; freopen_s(&f, "CONOUT$", "wb", stderr);
  687. if(active)WindowActivate(); // opening console switches focus to it, so if the app was active, then reactivate it so we won't lose the focus
  688. }
  689. }
  690. #endif
  691. }
  692. static SyncLock LogLock;
  693. void LogName(C Str &name) {Set(LogFile, name);}
  694. Str LogName( ) {return LogFile ;}
  695. void LogDel ( ) {FDelFile(LogFile);}
  696. void Log (C Str &text)
  697. {
  698. if(text.is())
  699. {
  700. // set text
  701. Str t;
  702. #if HAS_THREADS
  703. if(LogThreadID)
  704. {
  705. t="ThreadID: ";
  706. UIntPtr thread_id=GetThreadId();
  707. t+=((thread_id==App.threadID()) ? "Main (0x"
  708. : "Secondary (0x");
  709. t+=TextHex(thread_id, SIZE(thread_id)*2);
  710. t+="), ";
  711. }
  712. #endif
  713. if(LogDate || LogTime)
  714. {
  715. DateTime dt; dt.getLocal();
  716. if(LogDate && LogTime)t+=S+"DateTime ("+dt.asText();else
  717. if(LogDate )t+=S+"Date (" +TextInt(dt.year )+'-'+TextInt(dt.month , 2)+'-'+TextInt(dt.day , 2);else
  718. t+=S+"Time (" +TextInt(dt.hour, 2)+':'+TextInt(dt.minute, 2)+':'+TextInt(dt.second, 2);
  719. t+="), ";
  720. }
  721. if(LogCurTime)
  722. {
  723. t+=S+"CurTime ("+Time.curTime()+"), ";
  724. }
  725. t+=text;
  726. // write to system log
  727. #if WINDOWS
  728. OutputDebugString(t);
  729. #if WINDOWS_OLD
  730. if(LogConsoleOn)fputs(Str8(t), stdout);
  731. #endif
  732. #elif APPLE
  733. if(NSStringAuto str=t)NSLog(@"%@", str());
  734. #elif LINUX
  735. fputs(UTF8(t), stdout); fflush(stdout); // without the flush messages won't be displayed immediately
  736. #elif ANDROID
  737. Memc<Str> lines=Split(t, '\n'); // android has limit for too long messages
  738. FREPA(lines){Str8 line=UTF8(lines[i]); if(line.is())__android_log_write(ANDROID_LOG_INFO, "Esenthel", line.is() ? line : " ");} // '__android_log_write' will crash if text is null or ""
  739. #elif WEB
  740. fputs(UTF8(t), stdout);
  741. #endif
  742. // write to file log
  743. if(Is(LogFile))
  744. {
  745. FileText f; SyncLocker lock(LogLock); if(f.append(LogFile)) // use lock to prevent 2 threads appending the same file at the same time
  746. {
  747. #if LINUX
  748. f.fix_new_line=false; // on Linux disable Win style line endings because its Text Editor doesn't display them correctly in some cases
  749. #endif
  750. f.putText(t);
  751. }
  752. }
  753. }
  754. }
  755. void LogN(C Str &text)
  756. {
  757. Log(text+"\n");
  758. }
  759. void LogShow(Bool thread_id, Bool date, Bool time, Bool cur_time) {LogThreadID=thread_id; LogDate=date; LogTime=time; LogCurTime=cur_time;}
  760. /******************************************************************************/
  761. UInt Ceil2 (UInt x ) {return (x+ 1)&(~ 1);}
  762. UInt Ceil4 (UInt x ) {return (x+ 3)&(~ 3);}
  763. UInt Ceil8 (UInt x ) {return (x+ 7)&(~ 7);}
  764. UInt Ceil16 (UInt x ) {return (x+ 15)&(~ 15);}
  765. UInt Ceil32 (UInt x ) {return (x+ 31)&(~ 31);}
  766. UInt Ceil64 (UInt x ) {return (x+ 63)&(~ 63);}
  767. UInt Ceil128 (UInt x ) {return (x+127)&(~127);}
  768. UInt CeilPow2 (UInt x ) {UInt b= 1; for(; b<x && b<0x80000000; )b<<=1; return b;}
  769. UInt FloorPow2 (UInt x ) {UInt b=0x80000000; for(; b>x ; )b>>=1; return b;}
  770. UInt NearestPow2(UInt x ) {UInt fp2=FloorPow2(x); if(fp2!=0x80000000 && x>fp2+(fp2>>1))fp2<<=1; return fp2;} // must be > to return correct value for "x==1"
  771. Bool IsPow2 (UInt x ) {return !(x&(x-1));}
  772. Int Log2Ceil (UInt x ) {Int i=BitHi(x); if(x>(1u <<i))i++; return i;}
  773. Int Log2Ceil (ULong x ) {Int i=BitHi(x); if(x>(1ull<<i))i++; return i;}
  774. UInt Shl (UInt x, Int i) {return (i>=0) ? ((i<32) ? x<<i : 0) : ((i>-32) ? x>>-i : 0);}
  775. UInt Shr (UInt x, Int i) {return (i>=0) ? ((i<32) ? x>>i : 0) : ((i>-32) ? x<<-i : 0);}
  776. UInt Rol (UInt x, Int i) {i&=31; return (x<<i) | (x>>(32-i));}
  777. UInt Ror (UInt x, Int i) {i&=31; return (x>>i) | (x<<(32-i));}
  778. Int BitHi(UInt x)
  779. {
  780. #if WINDOWS
  781. if(!x)return 0; DWORD i; _BitScanReverse(&i, x); return i;
  782. #elif 1
  783. return x ? 31^__builtin_clz(x) : 0; // 31^__builtin_clz(x)==31-__builtin_clz(x)
  784. #else
  785. Int i=0; for(UInt bit=16; bit; bit>>=1)if(x>=(1<<bit)){i|=bit; x>>=bit;} return i;
  786. #endif
  787. }
  788. Int BitHi(ULong x)
  789. {
  790. #if WINDOWS && X64
  791. if(!x)return 0; DWORD i; _BitScanReverse64(&i, x); return i;
  792. #elif !WINDOWS
  793. return x ? 63^__builtin_clzll(x) : 0; // 63^__builtin_clzll(x)==63-__builtin_clzll(x)
  794. #else
  795. Int i=0; for(UInt bit=32; bit; bit>>=1)if(x>=(1ull<<bit)){i|=bit; x>>=bit;} return i;
  796. #endif
  797. }
  798. Int BitLo(UInt x)
  799. {
  800. #if WINDOWS
  801. if(!x)return 31; DWORD i; _BitScanForward(&i, x); return i;
  802. #elif 1
  803. return x ? __builtin_ctz(x) : 31;
  804. #else
  805. Int i=0; for(UInt bit=16; bit; bit>>=1)if(!(x&((1<<bit)-1))){i|=bit; x>>=bit;} return i;
  806. #endif
  807. }
  808. Int BitLo(ULong x)
  809. {
  810. #if WINDOWS && X64
  811. if(!x)return 63; DWORD i; _BitScanForward64(&i, x); return i;
  812. #elif !WINDOWS
  813. return x ? __builtin_ctzll(x) : 63;
  814. #else
  815. Int i=0; for(UInt bit=32; bit; bit>>=1)if(!(x&((1ull<<bit)-1))){i|=bit; x>>=bit;} return i;
  816. #endif
  817. }
  818. Int ByteHi(UInt x)
  819. {
  820. if(x>0xFFFFFF)return 4;
  821. if(x>0x00FFFF)return 3;
  822. if(x>0x0000FF)return 2;
  823. if(x>0x000000)return 1;
  824. return 0;
  825. }
  826. Int ByteHi(ULong x)
  827. {
  828. if(x>0xFFFFFFFFFFFFFF)return 8;
  829. if(x>0x00FFFFFFFFFFFF)return 7;
  830. if(x>0x0000FFFFFFFFFF)return 6;
  831. if(x>0x000000FFFFFFFF)return 5;
  832. if(x>0x00000000FFFFFF)return 4;
  833. if(x>0x0000000000FFFF)return 3;
  834. if(x>0x000000000000FF)return 2;
  835. if(x>0x00000000000000)return 1;
  836. return 0;
  837. }
  838. /******************************************************************************/
  839. Int MidMod(Int x, Int min, Int max)
  840. {
  841. return min+Mod(x-min, max-min+1);
  842. }
  843. /******************************************************************************/
  844. #if WINDOWS_NEW
  845. struct ClipSetter
  846. {
  847. Windows::ApplicationModel::DataTransfer::DataPackage ^content;
  848. ClipSetter(Windows::ApplicationModel::DataTransfer::DataPackage ^content)
  849. {
  850. T.content=content;
  851. try // can crash if app not yet initialized
  852. {
  853. auto task=create_task(Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]()
  854. {
  855. Windows::ApplicationModel::DataTransfer::Clipboard::SetContent(T.content);
  856. })));
  857. task.wait();
  858. }
  859. catch(...){}
  860. }
  861. };
  862. struct ClipGetter
  863. {
  864. Str text;
  865. Bool ok;
  866. Windows::ApplicationModel::DataTransfer::DataPackageView ^content;
  867. ClipGetter()
  868. {
  869. Bool main_thread=App.mainThread();
  870. if( main_thread) // 'GetContent' is single threaded and can be called only on the main thread
  871. {
  872. try // can crash if app not focused
  873. {
  874. content=Windows::ApplicationModel::DataTransfer::Clipboard::GetContent();
  875. }
  876. catch(...){}
  877. }else
  878. {
  879. try // can crash if app not yet initialized
  880. {
  881. auto task=create_task(Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]()
  882. {
  883. try // can crash if app not focused
  884. {
  885. content=Windows::ApplicationModel::DataTransfer::Clipboard::GetContent();
  886. }
  887. catch(...){}
  888. })));
  889. task.wait();
  890. }
  891. catch(...){}
  892. }
  893. if(content && content->Contains(Windows::ApplicationModel::DataTransfer::StandardDataFormats::Text))
  894. {
  895. auto task=create_task(content->GetTextAsync());
  896. if(main_thread)
  897. {
  898. ok=false;
  899. task.then([this](Platform::String ^text)
  900. {
  901. T.text=text->Data();
  902. ok=true;
  903. });
  904. App.loopUntil(ok);
  905. }else text=task.get()->Data();
  906. }
  907. }
  908. };
  909. #endif
  910. Bool ClipSet(C Str &text, Bool fix_new_line)
  911. {
  912. #if WINDOWS_OLD
  913. if(OpenClipboard(null))
  914. {
  915. EmptyClipboard();
  916. Str temp; C Str *t=&text; if(fix_new_line)t=&(temp=FixNewLine(text));
  917. Int max_length=t->length()+1;
  918. if( max_length>1)if(HGLOBAL buf=GlobalAlloc(GMEM_MOVEABLE, max_length*SIZE(Char)))
  919. {
  920. Set((Char*)GlobalLock(buf), (*t)(), max_length); GlobalUnlock(buf);
  921. SetClipboardData(CF_UNICODETEXT, buf);
  922. //GlobalFree(buf); this shouldn't be called after SetClipboardData
  923. }
  924. CloseClipboard();
  925. return true;
  926. }
  927. return false;
  928. #elif WINDOWS_NEW
  929. auto content=ref new Windows::ApplicationModel::DataTransfer::DataPackage;
  930. content->SetText(ref new Platform::String(fix_new_line ? FixNewLine(text) : text));
  931. if(App.mainThread())Windows::ApplicationModel::DataTransfer::Clipboard::SetContent(content);else ClipSetter cs(content);
  932. return true;
  933. #elif MAC
  934. PasteboardClear(Pasteboard);
  935. if(text.is())
  936. {
  937. PasteboardSynchronize(Pasteboard);
  938. CFDataRef data=CFDataCreate(kCFAllocatorDefault, (UInt8*)text(), text.length()*SIZE(Char));
  939. PasteboardPutItemFlavor(Pasteboard, (PasteboardItemID)1, CFSTR("public.utf16-plain-text"), data, 0);
  940. if(data)CFRelease(data);
  941. }
  942. return true;
  943. #elif LINUX
  944. // TODO: this text will disappear once the application gets closed
  945. if(XDisplay && App.hwnd())
  946. if(Atom FIND_ATOM(UTF8_STRING))
  947. {
  948. Atom FIND_ATOM(CLIPBOARD);
  949. Str8 utf=UTF8(text);
  950. int ok=XChangeProperty(XDisplay, DefaultRootWindow(XDisplay), XA_CUT_BUFFER0, UTF8_STRING, 8, PropModeReplace, (const unsigned char*)utf(), utf.length());
  951. if(CLIPBOARD && XGetSelectionOwner(XDisplay, CLIPBOARD )!=App.Hwnd())XSetSelectionOwner(XDisplay, CLIPBOARD , App.Hwnd(), CurrentTime);
  952. if( XGetSelectionOwner(XDisplay, XA_PRIMARY)!=App.Hwnd())XSetSelectionOwner(XDisplay, XA_PRIMARY, App.Hwnd(), CurrentTime);
  953. return ok==1;
  954. }
  955. return false;
  956. #elif IOS
  957. Bool ok=false; if(NSStringAuto str=text)if(UIPasteboard *pasteboard=[UIPasteboard generalPasteboard]){[pasteboard setString:str]; ok=true;}
  958. return ok;
  959. #elif ANDROID
  960. JNI jni;
  961. if( jni && ClipboardManager) // system clipboard supported
  962. {
  963. #if 1
  964. if(JMethodID setText=jni->GetMethodID(ClipboardManagerClass, "setText", "(Ljava/lang/CharSequence;)V"))
  965. if(JString jtext=JString(jni, text))
  966. {
  967. jni->CallVoidMethod(ClipboardManager, setText, jtext());
  968. return true;
  969. }
  970. #else
  971. if(JClass ClipDataClass=JClass(jni, "android/content/ClipData"))
  972. if(JMethodID newPlainText=jni->GetStaticMethodID(ClipDataClass, "newPlainText", "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;"))
  973. if(JString label=JString(jni, "Esenthel"))
  974. if(JString jtext=JString(jni, text))
  975. if(JObject ClipData=JObject(jni, jni->CallStaticObjectMethod(ClipDataClass, newPlainText, label(), jtext())))
  976. if(JMethodID setPrimaryClip=jni->GetMethodID(ClipboardManagerClass, "setPrimaryClip", "(Landroid/content/ClipData;)V"))
  977. {
  978. jni->CallVoidMethod(ClipboardManager, setPrimaryClip, ClipData());
  979. return true;
  980. }
  981. #endif
  982. return false;
  983. }
  984. Clipboard=text;
  985. return true;
  986. #else
  987. Clipboard=text;
  988. return true;
  989. #endif
  990. }
  991. Str ClipGet(Bool fix_new_line)
  992. {
  993. Str s;
  994. #if WINDOWS_OLD
  995. if(IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(null))
  996. {
  997. if(HANDLE hData=GetClipboardData(CF_UNICODETEXT))
  998. {
  999. s=(Char*)GlobalLock (hData);
  1000. GlobalUnlock(hData);
  1001. if(fix_new_line)s.replace('\r', '\0');
  1002. }
  1003. CloseClipboard();
  1004. }
  1005. #elif WINDOWS_NEW
  1006. ClipGetter cg; if(fix_new_line)cg.text.replace('\r', '\0');
  1007. return cg.text;
  1008. #elif MAC
  1009. #if 1
  1010. if(NSPasteboard *pasteboard=[NSPasteboard generalPasteboard])
  1011. return [pasteboard stringForType:NSPasteboardTypeString]; // don't release this as it will crash
  1012. #else // this does not properly support new lines (when accessed from Xcode for example)
  1013. Bool found=false;
  1014. PasteboardSynchronize(Pasteboard);
  1015. ItemCount itemCount; PasteboardGetItemCount(Pasteboard, &itemCount);
  1016. for(UInt32 itemIndex=1; itemIndex<=itemCount && !found; itemIndex++)
  1017. {
  1018. PasteboardItemID itemID ; PasteboardGetItemIdentifier(Pasteboard, itemIndex, &itemID);
  1019. CFArrayRef flavorTypeArray; PasteboardCopyItemFlavors(Pasteboard, itemID, &flavorTypeArray);
  1020. CFIndex flavorCount=CFArrayGetCount(flavorTypeArray);
  1021. for(CFIndex flavorIndex=0; flavorIndex<flavorCount && !found; flavorIndex++)
  1022. {
  1023. CFStringRef flavorType=(CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
  1024. if(UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text")))
  1025. {
  1026. found=true;
  1027. CFDataRef flavorData; PasteboardCopyItemFlavorData(Pasteboard, itemID, flavorType, &flavorData);
  1028. CFIndex flavorDataSize=CFDataGetLength(flavorData);
  1029. Char *src =(Char*)CFDataGetBytePtr(flavorData);
  1030. REP(flavorDataSize/SIZE(Char))s+=*src++;
  1031. if(flavorData)CFRelease(flavorData);
  1032. }
  1033. }
  1034. if(flavorTypeArray)CFRelease(flavorTypeArray);
  1035. }
  1036. #endif
  1037. #elif LINUX
  1038. if(XDisplay)
  1039. if(Atom FIND_ATOM(CLIPBOARD))
  1040. if(Atom FIND_ATOM(UTF8_STRING))
  1041. {
  1042. Atom selection;
  1043. XWindow owner=XGetSelectionOwner(XDisplay, CLIPBOARD);
  1044. if(!owner || owner==App.Hwnd() || !App.hwnd())
  1045. {
  1046. owner=DefaultRootWindow(XDisplay);
  1047. selection=XA_CUT_BUFFER0;
  1048. }else
  1049. {
  1050. owner=App.Hwnd();
  1051. Atom GET_ATOM(EE_SELECTION); selection=EE_SELECTION;
  1052. XConvertSelection(XDisplay, CLIPBOARD, UTF8_STRING, EE_SELECTION, owner, CurrentTime);
  1053. XSync(XDisplay, false);
  1054. REP(1024) // attempts to check if window got selection, otherwise get window property will fail (this is still needed even though 'XSync' is used)
  1055. {
  1056. XEvent event; if(XCheckTypedWindowEvent(XDisplay, App.Hwnd(), SelectionNotify, &event))break;
  1057. usleep(1);
  1058. }
  1059. }
  1060. Atom type;
  1061. int format=0;
  1062. unsigned long items=0;
  1063. unsigned long overflow=0;
  1064. unsigned char *data=null;
  1065. if(!XGetWindowProperty(XDisplay, owner, selection, 0, INT_MAX/4, false, UTF8_STRING, &type, &format, &items, &overflow, &data))
  1066. if(type==UTF8_STRING)s=FromUTF8((char*)data);
  1067. if(data)XFree(data);
  1068. }
  1069. #elif IOS
  1070. if(UIPasteboard *pasteboard=[UIPasteboard generalPasteboard])return pasteboard.string;
  1071. #elif ANDROID
  1072. JNI jni;
  1073. if( jni && ClipboardManager) // system clipboard supported
  1074. {
  1075. #if 1
  1076. if(JMethodID getText=jni->GetMethodID(ClipboardManagerClass, "getText", "()Ljava/lang/CharSequence;"))
  1077. if(JString text=JString(jni, jni->CallObjectMethod(ClipboardManager, getText)))
  1078. return text.str();
  1079. #else
  1080. if(JClass ClipDataClass=JClass(jni, "android/content/ClipData"))
  1081. if(JMethodID getPrimaryClip=jni->GetMethodID(ClipboardManagerClass, "getPrimaryClip", "()Landroid/content/ClipData;"))
  1082. if(JObject ClipData=JObject(jni, jni->CallObjectMethod(ClipboardManager, getPrimaryClip)))
  1083. if(JMethodID getItemAt=jni->GetMethodID(ClipDataClass, "getItemAt", "(I)Landroid/content/ClipData$Item;"))
  1084. if(JObject ClipDataItem=JObject(jni, jni->CallObjectMethod(ClipData, getItemAt, jint(0))))
  1085. if(JClass ClipDataItemClass=JClass(jni, ClipDataItem))
  1086. if(JMethodID getText=jni->GetMethodID(ClipDataItemClass, "getText", "()Ljava/lang/CharSequence;"))
  1087. if(JString text=JString(jni, jni->CallObjectMethod(ClipDataItem, getText)))
  1088. return text.str();
  1089. #endif
  1090. }
  1091. return Clipboard;
  1092. #else
  1093. return Clipboard;
  1094. #endif
  1095. return s;
  1096. }
  1097. /******************************************************************************/
  1098. VecI4 OSVerNumber()
  1099. {
  1100. #if WINDOWS_OLD
  1101. #if 1 // faster
  1102. DWORD ver=GetVersion();
  1103. return VecI4(LOBYTE(LOWORD(ver)), HIBYTE(LOWORD(ver)), 0, 0);
  1104. #else // slower
  1105. OSVERSIONINFOEX v; Zero(v);
  1106. v.dwOSVersionInfoSize=SIZE(v);
  1107. if(GetVersionEx((OSVERSIONINFO*)&v))return VecI4(v.dwMajorVersion, v.dwMinorVersion, 0, 0);
  1108. #endif
  1109. #elif WINDOWS_NEW
  1110. return VecI4(10, 0, 0, 0);
  1111. #elif ANDROID
  1112. return VecI4(AndroidSDK, 0, 0, 0);
  1113. #elif MAC
  1114. // TODO:
  1115. #elif IOS
  1116. if(NSString *ver=[[UIDevice currentDevice] systemVersion]) // don't release this
  1117. {
  1118. Str v=ver; // this returns for example "8.1.3"
  1119. v.replace('.', ',');
  1120. return TextVecI4(v);
  1121. }
  1122. #endif
  1123. return 0;
  1124. }
  1125. OS_VER OSVer()
  1126. {
  1127. #if WINDOWS_OLD
  1128. OSVERSIONINFOEX v; Zero(v);
  1129. v.dwOSVersionInfoSize=SIZE(v);
  1130. if(GetVersionEx((OSVERSIONINFO*)&v))
  1131. {
  1132. if(v.dwMajorVersion==10)
  1133. {
  1134. if(v.dwMinorVersion==0)return (v.wProductType==VER_NT_WORKSTATION) ? WINDOWS_10 : WINDOWS_SERVER_2016;
  1135. }else
  1136. if(v.dwMajorVersion==6)
  1137. {
  1138. if(v.dwMinorVersion==0)return (v.wProductType==VER_NT_WORKSTATION) ? WINDOWS_VISTA : WINDOWS_SERVER_2008;
  1139. if(v.dwMinorVersion==1)return (v.wProductType==VER_NT_WORKSTATION) ? WINDOWS_7 : WINDOWS_SERVER_2008_R2;
  1140. if(v.dwMinorVersion==2)return (v.wProductType==VER_NT_WORKSTATION) ? WINDOWS_8 : WINDOWS_SERVER_2012;
  1141. if(v.dwMinorVersion==3)return (v.wProductType==VER_NT_WORKSTATION) ? WINDOWS_8/*_1*/ : WINDOWS_SERVER_2012_R2;
  1142. }else
  1143. if(v.dwMajorVersion==5)
  1144. {
  1145. if(v.dwMinorVersion==2)
  1146. {
  1147. if(v.wProductType==VER_NT_WORKSTATION)return WINDOWS_XP_64;
  1148. if(GetSystemMetrics(SM_SERVERR2) )return WINDOWS_SERVER_2003_R2;
  1149. return WINDOWS_SERVER_2003;
  1150. }else
  1151. if(v.dwMinorVersion==1)return WINDOWS_XP ;else
  1152. if(v.dwMinorVersion==0)return WINDOWS_2000;
  1153. }
  1154. }
  1155. return WINDOWS_UNKNOWN;
  1156. #elif WINDOWS_NEW
  1157. return WINDOWS_10;
  1158. #elif MAC
  1159. return OS_MAC;
  1160. #elif LINUX
  1161. return OS_LINUX;
  1162. #elif ANDROID
  1163. // SDK API levels taken from:
  1164. // http://developer.android.com/guide/appendix/api-levels.html
  1165. // http://en.wikipedia.org/wiki/Android_version_history#Version_history_by_API_level
  1166. if(AndroidSDK>=28)return ANDROID_PIE;
  1167. if(AndroidSDK>=26)return ANDROID_OREO;
  1168. if(AndroidSDK>=24)return ANDROID_NOUGAT;
  1169. if(AndroidSDK>=23)return ANDROID_MARSHMALLOW;
  1170. if(AndroidSDK>=21)return ANDROID_LOLLIPOP;
  1171. if(AndroidSDK>=19)return ANDROID_KIT_KAT;
  1172. if(AndroidSDK>=16)return ANDROID_JELLY_BEAN;
  1173. if(AndroidSDK>=14)return ANDROID_ICE_CREAM_SANDWICH;
  1174. if(AndroidSDK>=11)return ANDROID_HONEYCOMB;
  1175. if(AndroidSDK>= 9)return ANDROID_GINGERBREAD;
  1176. return ANDROID_UNKNOWN;
  1177. #elif IOS
  1178. return OS_IOS;
  1179. #elif WEB
  1180. switch(EM_ASM_INT
  1181. (
  1182. if(navigator.platform.indexOf('Win')==0)return 0;
  1183. if(navigator.platform.indexOf('Mac')==0)return 1;
  1184. if(navigator.appVersion.indexOf('Android')>=0)return 3; // !! test this before Linux !!
  1185. if(navigator.platform.indexOf('Linux')==0)return 2;
  1186. if(navigator.platform.indexOf('iPhone')==0)return 4;
  1187. return -1;
  1188. ))
  1189. {
  1190. case 0: return WINDOWS_UNKNOWN;
  1191. case 1: return OS_MAC;
  1192. case 2: return OS_LINUX;
  1193. case 3: return ANDROID_UNKNOWN;
  1194. case 4: return OS_IOS;
  1195. }
  1196. #endif
  1197. return OS_UNKNOWN;
  1198. }
  1199. OS_VER OSGroup(OS_VER ver)
  1200. {
  1201. if(OSWindows(ver))return WINDOWS_UNKNOWN;
  1202. if(OSAndroid(ver))return ANDROID_UNKNOWN;
  1203. return ver;
  1204. }
  1205. CChar8* OSName(OS_VER ver)
  1206. {
  1207. switch(ver)
  1208. {
  1209. default : return "Unknown"; // OS_UNKNOWN
  1210. case WINDOWS_UNKNOWN : return "Windows";
  1211. case WINDOWS_2000 : return "Windows 2000";
  1212. case WINDOWS_XP : return "Windows XP";
  1213. case WINDOWS_XP_64 : return "Windows XP 64";
  1214. case WINDOWS_VISTA : return "Windows Vista";
  1215. case WINDOWS_7 : return "Windows 7";
  1216. case WINDOWS_8 : return "Windows 8";
  1217. //case WINDOWS_8_1 : return "Windows 8.1";
  1218. case WINDOWS_10 : return "Windows 10";
  1219. case WINDOWS_SERVER_2003 : return "Windows Server 2003";
  1220. case WINDOWS_SERVER_2003_R2 : return "Windows Server 2003 R2";
  1221. case WINDOWS_SERVER_2008 : return "Windows Server 2008";
  1222. case WINDOWS_SERVER_2008_R2 : return "Windows Server 2008 R2";
  1223. case WINDOWS_SERVER_2012 : return "Windows Server 2012";
  1224. case WINDOWS_SERVER_2012_R2 : return "Windows Server 2012 R2";
  1225. case WINDOWS_SERVER_2016 : return "Windows Server 2016";
  1226. case OS_MAC : return "Mac";
  1227. case OS_LINUX : return "Linux";
  1228. case ANDROID_UNKNOWN : return "Android";
  1229. case ANDROID_GINGERBREAD : return "Android Gingerbread";
  1230. case ANDROID_HONEYCOMB : return "Android Honeycomb";
  1231. case ANDROID_ICE_CREAM_SANDWICH: return "Android Ice Cream Sandwich";
  1232. case ANDROID_JELLY_BEAN : return "Android Jelly Bean";
  1233. case ANDROID_KIT_KAT : return "Android Kit Kat";
  1234. case ANDROID_LOLLIPOP : return "Android Lollipop";
  1235. case ANDROID_MARSHMALLOW : return "Android Marshmallow";
  1236. case ANDROID_NOUGAT : return "Android Nougat";
  1237. case ANDROID_OREO : return "Android Oreo";
  1238. case ANDROID_PIE : return "Android Pie";
  1239. case OS_IOS : return "iOS";
  1240. }
  1241. }
  1242. Bool OSWindows(OS_VER ver) {return ver>=WINDOWS_UNKNOWN && ver<=WINDOWS_SERVER_2016;}
  1243. Bool OSMac (OS_VER ver) {return ver==OS_MAC;}
  1244. Bool OSLinux (OS_VER ver) {return ver==OS_LINUX;}
  1245. Bool OSAndroid(OS_VER ver) {return ver>=ANDROID_UNKNOWN && ver<=ANDROID_PIE;}
  1246. Bool OSiOS (OS_VER ver) {return ver==OS_IOS;}
  1247. /******************************************************************************/
  1248. #if WINDOWS_NEW
  1249. static struct UserNameGetter
  1250. {
  1251. Str name;
  1252. Bool is;
  1253. SyncLock lock;
  1254. C Str& get(Bool wait=true)
  1255. {
  1256. if(!is && App.hwnd()) // !! we can call this only after window was created, otherwise this will crash !!
  1257. {
  1258. if(App.mainThread()) // 'User::FindAllAsync' can be called only on the main thread
  1259. {
  1260. create_task(User::FindAllAsync(UserType::LocalUser, UserAuthenticationStatus::LocallyAuthenticated)).then([this](IVectorView<User^>^ users)
  1261. {
  1262. if(users->Size>0)
  1263. {
  1264. auto properties = ref new Vector<Platform::String^>();
  1265. properties->Append(KnownUserProperties::FirstName);
  1266. properties->Append(KnownUserProperties:: LastName);
  1267. create_task(users->First()->Current->GetPropertiesAsync(properties->GetView())).then([this](IPropertySet^ values)
  1268. {
  1269. if(auto first_name=safe_cast<Platform::String^>(values->Lookup(KnownUserProperties::FirstName)))
  1270. if(auto last_name=safe_cast<Platform::String^>(values->Lookup(KnownUserProperties:: LastName)))
  1271. {
  1272. SyncLocker locker(lock); // sync to avoid modifying the name on multiple threads
  1273. if(!is){name=first_name->Data(); name.space()+=last_name->Data(); is=true;} // set 'is' at the end, once 'name' is ready, because as soon as 'is' is true, then other threads may access the name, also set 'is' here as well even though we're setting it later, to avoid modifying/reading the name on multiple threads
  1274. }
  1275. is=true; // set this in case there's no FirstName/LastName available
  1276. });
  1277. }else is=true; // set this in case there are no users available
  1278. });
  1279. }else
  1280. {
  1281. auto task=create_task(Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]()
  1282. {
  1283. // we're on the main thread now, so just call 'get' again, but this time don't wait
  1284. get(false); // !! we can't and don't need to wait here, as we only want to request user data so it can be obtained back on the caller thread, if we wait, then crash will occur, because this is actually called inside a Windows Callback on the main thread, and if we wait then 'loopUntil' would call Windows Callbacks again
  1285. })));
  1286. }
  1287. if(wait)
  1288. if(App.mainThread())App.loopUntil(is, true);else for(; !is; )Time.wait(1); // wait because app may have to wait a long time until user agrees to provide permission which would cause full CPU usage
  1289. }
  1290. return name;
  1291. }
  1292. }UserName;
  1293. #elif ANDROID
  1294. static SyncLock AndroidUserNameLock;
  1295. static Str AndroidUserName;
  1296. #endif
  1297. Str OSUserName(Bool short_name)
  1298. {
  1299. #if WINDOWS_OLD
  1300. wchar_t user_name[256+1]; DWORD size=Elms(user_name);
  1301. if(short_name ? GetUserName(user_name, &size) : GetUserNameEx(NameDisplay, user_name, &size))return user_name;
  1302. return S;
  1303. #elif WINDOWS_NEW // calling this method will result in asking for permission on the screen
  1304. return UserName.get();
  1305. #elif MAC
  1306. Char8 name[MAX_UTF_PATH]; name[0]=0;
  1307. #if 0
  1308. getlogin_r(name, Elms(name));
  1309. #else
  1310. if(CFStringRef cf_name=CSCopyUserName(short_name))
  1311. {
  1312. CFStringGetCString(cf_name, name, Elms(name), kCFStringEncodingUTF8);
  1313. CFRelease(cf_name);
  1314. }
  1315. #endif
  1316. return FromUTF8(name);
  1317. #elif LINUX
  1318. __uid_t uid=geteuid();
  1319. passwd pw, *pwp;
  1320. Char8 temp[4096];
  1321. if(!getpwuid_r(uid, &pw, temp, SIZE(temp), &pwp))
  1322. {
  1323. if(!short_name) // Linux may add unnecessary commas at the end "name,,,"
  1324. {
  1325. for(Int i=SetReturnLength(temp, pw.pw_gecos); i>0 && temp[i-1]==','; )temp[--i]='\0';
  1326. return FromUTF8(temp);
  1327. }
  1328. return FromUTF8(pw.pw_name);
  1329. }
  1330. return S;
  1331. #elif IOS
  1332. return [[UIDevice currentDevice] name]; // don't release this NSString as it causes errors
  1333. #elif ANDROID
  1334. if(!AndroidUserName.is())
  1335. {
  1336. SyncLocker locker(AndroidUserNameLock);
  1337. if(!AndroidUserName.is())
  1338. {
  1339. RequirePermission(PERMISSION_USER_NAME);
  1340. JNI jni;
  1341. if(jni && Activity)
  1342. if(JClass AccountManagerClass=JClass(jni, "android/accounts/AccountManager"))
  1343. if(JClass AccountClass=JClass(jni, "android/accounts/Account"))
  1344. if(JMethodID get=jni->GetStaticMethodID(AccountManagerClass, "get", "(Landroid/content/Context;)Landroid/accounts/AccountManager;"))
  1345. if(JObject mgr=JObject(jni, jni->CallStaticObjectMethod(AccountManagerClass, get, Activity)))
  1346. if(JMethodID getAccountsByType=jni->GetMethodID(AccountManagerClass, "getAccountsByType", "(Ljava/lang/String;)[Landroid/accounts/Account;"))
  1347. if(JString com_google=JString(jni, "com.google"))
  1348. if(JObjectArray accounts=JObjectArray(jni, jni->CallObjectMethod(mgr, getAccountsByType, com_google())))
  1349. {
  1350. Int length=accounts.elms();
  1351. if( length>=1)
  1352. if(JObject account=JObject(jni, accounts[0]))
  1353. if(JFieldID name=jni->GetFieldID(AccountClass, "name", "Ljava/lang/String;"))
  1354. if(JString n=JString(jni, jni->GetObjectField(account, name)))
  1355. AndroidUserName=n.str();
  1356. }
  1357. }
  1358. }
  1359. return AndroidUserName;
  1360. #else
  1361. return S;
  1362. #endif
  1363. }
  1364. /******************************************************************************/
  1365. static struct Locale
  1366. {
  1367. LANG_TYPE lang;
  1368. CChar8 *code;
  1369. }locale[]=
  1370. {
  1371. {(LANG_TYPE)LANG_AFRIKAANS , "af"},
  1372. {(LANG_TYPE)LANG_ARABIC , "ar"},
  1373. {(LANG_TYPE)LANG_ARMENIAN , "hy"},
  1374. {(LANG_TYPE)LANG_BELARUSIAN, "be"},
  1375. {(LANG_TYPE)LANG_BULGARIAN , "bg"},
  1376. {(LANG_TYPE)LANG_CHINESE , "zh"},
  1377. {(LANG_TYPE)LANG_CROATIAN , "hr"},
  1378. {(LANG_TYPE)LANG_CZECH , "cs"},
  1379. {(LANG_TYPE)LANG_DANISH , "da"},
  1380. {(LANG_TYPE)LANG_DUTCH , "nl"},
  1381. {(LANG_TYPE)LANG_ENGLISH , "en"},
  1382. {(LANG_TYPE)LANG_ESTONIAN , "et"},
  1383. {(LANG_TYPE)LANG_FILIPINO , "tl"},
  1384. {(LANG_TYPE)LANG_FINNISH , "fi"},
  1385. {(LANG_TYPE)LANG_FRENCH , "fr"},
  1386. {(LANG_TYPE)LANG_GALICIAN , "gl"},
  1387. {(LANG_TYPE)LANG_GERMAN , "de"},
  1388. {(LANG_TYPE)LANG_GREEK , "el"},
  1389. {(LANG_TYPE)LANG_HEBREW , "he"},
  1390. {(LANG_TYPE)LANG_HUNGARIAN , "hu"},
  1391. {(LANG_TYPE)LANG_HINDI , "hi"},
  1392. {(LANG_TYPE)LANG_ICELANDIC , "is"},
  1393. {(LANG_TYPE)LANG_INDONESIAN, "id"},
  1394. {(LANG_TYPE)LANG_IRISH , "ga"},
  1395. {(LANG_TYPE)LANG_ITALIAN , "it"},
  1396. {(LANG_TYPE)LANG_JAPANESE , "ja"},
  1397. {(LANG_TYPE)LANG_KOREAN , "ko"},
  1398. {(LANG_TYPE)LANG_LATVIAN , "lv"},
  1399. {(LANG_TYPE)LANG_LITHUANIAN, "lt"},
  1400. {(LANG_TYPE)LANG_MALAY , "ms"},
  1401. {(LANG_TYPE)LANG_MONGOLIAN , "mn"},
  1402. {(LANG_TYPE)LANG_NORWEGIAN , "nb"},
  1403. {(LANG_TYPE)LANG_NORWEGIAN , "nn"},
  1404. {(LANG_TYPE)LANG_PERSIAN , "fa"},
  1405. {(LANG_TYPE)LANG_POLISH , "pl"},
  1406. {(LANG_TYPE)LANG_PORTUGUESE, "pt"},
  1407. {(LANG_TYPE)LANG_RUSSIAN , "ru"},
  1408. {(LANG_TYPE)LANG_ROMANIAN , "ro"},
  1409. {(LANG_TYPE)LANG_SERBIAN , "sr"},
  1410. {(LANG_TYPE)LANG_SPANISH , "es"},
  1411. {(LANG_TYPE)LANG_SLOVAK , "sk"},
  1412. {(LANG_TYPE)LANG_SLOVENIAN , "sl"},
  1413. {(LANG_TYPE)LANG_SWAHILI , "sw"},
  1414. {(LANG_TYPE)LANG_SWEDISH , "sv"},
  1415. {(LANG_TYPE)LANG_TAMIL , "ta"},
  1416. {(LANG_TYPE)LANG_THAI , "th"},
  1417. {(LANG_TYPE)LANG_TURKISH , "tr"},
  1418. {(LANG_TYPE)LANG_UKRAINIAN , "uk"},
  1419. {(LANG_TYPE)LANG_VIETNAMESE, "vi"},
  1420. {(LANG_TYPE)LANG_XHOSA , "xh"},
  1421. {(LANG_TYPE)LANG_ZULU , "zu"},
  1422. };
  1423. LANG_TYPE LanguageCode(C Str &lang)
  1424. {
  1425. if(lang.is())FREPA(locale)if(Starts(lang, locale[i].code))return locale[i].lang;
  1426. return LANG_UNKNOWN;
  1427. }
  1428. CChar8* LanguageCode(LANG_TYPE lang)
  1429. {
  1430. if(lang)FREPA(locale)if(locale[i].lang==lang)return locale[i].code;
  1431. return null;
  1432. }
  1433. LANG_TYPE OSLanguage()
  1434. {
  1435. #if WINDOWS_OLD
  1436. return LANG_TYPE(GetSystemDefaultLangID()&0xFF);
  1437. #elif WINDOWS_NEW
  1438. if(auto langs=Windows::System::UserProfile::GlobalizationPreferences::Languages)
  1439. if(langs->Size>0)return LanguageCode(langs->GetAt(0)->Data());
  1440. #elif APPLE
  1441. LANG_TYPE lang=LANG_UNKNOWN;
  1442. if(CFArrayRef langs=CFLocaleCopyPreferredLanguages())
  1443. {
  1444. FREP(CFArrayGetCount(langs))
  1445. {
  1446. CFStringRef l=(CFStringRef)CFArrayGetValueAtIndex(langs, i);
  1447. Char8 name[MAX_UTF_PATH]; CFStringGetCString(l, name, Elms(name), kCFStringEncodingUTF8);
  1448. if(lang=LanguageCode(name))break;
  1449. }
  1450. CFRelease(langs);
  1451. }
  1452. return lang;
  1453. #elif ANDROID
  1454. if(AndroidApp && AndroidApp->config)
  1455. {
  1456. char code[3]={0, 0, 0}; AConfiguration_getLanguage(AndroidApp->config, code); // getLanguage fills only first 2 characters (they will not be zero-terminated)
  1457. return LanguageCode(code);
  1458. }
  1459. #elif LINUX || WEB
  1460. return LanguageCode(setlocale(LC_CTYPE, ""));
  1461. #endif
  1462. return LANG_UNKNOWN;
  1463. }
  1464. /******************************************************************************/
  1465. Str LanguageSpecific(LANG_TYPE lang)
  1466. {
  1467. switch(lang)
  1468. {
  1469. case PL : return u"ĄĆĘŁŃÓŚŻŹąćęłńóśżź";
  1470. case DE : return u"äÄëËöÖüÜßẞ";
  1471. case FR : return u"àÀáÁâÂãÃäÄåÅæÆçÇèÈéÉêÊëËìÌíÍîÎïÏðÐñÑòÒóÓôÔõÕöÖøØùÙúÚûÛüÜýÝ";
  1472. case RU : return u"аАбБвВгГдДеЕёЁжЖзЗиИйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩъЪыЫьЬэЭюЮяЯ";
  1473. case PO : return u"áÁàÀâÂãÃăĂçÇęĘéÉêÊíÍóÓőŐõÕôÔŕŔúÚ";
  1474. case LANG_GREEK: return u"αΑβΒγΓδΔεΕζΖηΗθΘιΙκΚλΛμΜνΝξΞοΟπΠρΡσΣτΤυΥφΦχΧψΨωΩ";
  1475. case LANG_THAI : return u"กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛"; // https://en.wikipedia.org/wiki/Thai_alphabet#Unicode
  1476. case CN:
  1477. {
  1478. // ranges provided by: Chinese company Moli
  1479. Str s; s.reserve(0xFFFF);
  1480. for(Int i=0x3400; i<=0x4DB5; i++)s+=Char(i);
  1481. for(Int i=0x4E00; i<=0x9FCB; i++)s+=Char(i);
  1482. for(Int i=0xF900; i<=0xFAD9; i++)s+=Char(i);
  1483. for(Int i=0x3000; i<=0x303F; i++)s+=Char(i); // punctuation (like '。' and 'FullWidthSpace')
  1484. return s;
  1485. }
  1486. case JP:
  1487. {
  1488. // ranges taken from: http://en.wikipedia.org/wiki/Japanese_writing_system
  1489. Str s; s.reserve(0xFFFF);
  1490. for(Int i=0x4E00; i<=0x9FBF; i++)s+=Char(i); // Kanji
  1491. for(Int i=0x3040; i<=0x309F; i++)s+=Char(i); // Hiragana
  1492. for(Int i=0x30A0; i<=0x30FF; i++)s+=Char(i); // Katakana
  1493. for(Int i=0x3000; i<=0x303F; i++)s+=Char(i); // punctuation (like '。' and 'FullWidthSpace')
  1494. return s;
  1495. }
  1496. case KO:
  1497. {
  1498. // ranges taken from: http://en.wikipedia.org/wiki/Hangul
  1499. Str s; s.reserve(0xFFFF);
  1500. for(Int i=0xAC00; i<=0xD7AF; i++)s+=Char(i);
  1501. for(Int i=0x1100; i<=0x11FF; i++)s+=Char(i);
  1502. for(Int i=0x3130; i<=0x318F; i++)s+=Char(i);
  1503. for(Int i=0x3200; i<=0x32FF; i++)s+=Char(i);
  1504. for(Int i=0xA960; i<=0xA97F; i++)s+=Char(i);
  1505. for(Int i=0xD7B0; i<=0xD7FF; i++)s+=Char(i);
  1506. for(Int i=0xFF00; i<=0xFFEF; i++)s+=Char(i);
  1507. return s;
  1508. }
  1509. }
  1510. return S;
  1511. }
  1512. /******************************************************************************/
  1513. static Bool SpecialLink(C Str &name)
  1514. {
  1515. // http://, https://, ftp://, mailto://, tel://, fb://, steam://, ms-settings://
  1516. FREPA(name)
  1517. {
  1518. Char c=name()[i]; // () avoids range checks
  1519. if(c>='a' && c<='z'
  1520. || c>='A' && c<='Z'
  1521. || c>='0' && c<='9'
  1522. || c=='-')continue;
  1523. if(c==':')return name[i+1]=='/' && name[i+2]=='/';
  1524. break;
  1525. }
  1526. return false;
  1527. }
  1528. Bool Explore(C Str &name, Bool select)
  1529. {
  1530. FileInfoSystem fi(name);
  1531. Bool file=(fi.type==FSTD_FILE);
  1532. #if MAC
  1533. file|=(fi.type==FSTD_DIR && Equal(_GetExt(name), "app")); // on Mac applications are actually folders
  1534. #endif
  1535. if(fi.type && (select || file)) // always select if this is a file
  1536. {
  1537. #if WINDOWS_OLD
  1538. if(auto item=ILCreateFromPath(MakeFullPath(name).replace('/', '\\'))) // 'MakeFullPath' is needed in case we're exploring relative to current dir, because even though 'FileInfoSystem' succeeds, the 'ILCreateFromPath' will fail
  1539. {
  1540. Bool ok=OK(SHOpenFolderAndSelectItems(item, 0, 0, 0)); ILFree(item);
  1541. return ok;
  1542. }
  1543. #elif MAC
  1544. Bool ok=false;
  1545. Str full=UnixPath(MakeFullPath(name));
  1546. if(NSStringAuto ns_name=full)
  1547. if(NSStringAuto ns_path=GetPath(full))
  1548. ok=[[NSWorkspace sharedWorkspace] selectFile:ns_name inFileViewerRootedAtPath:ns_path];
  1549. return ok;
  1550. #elif LINUX
  1551. // TODO: Linux Explore select
  1552. #endif
  1553. }
  1554. return (fi.type || SpecialLink(name)) ? Run(file ? GetPath(name) : name) : false; // this function was designed to never run programs so open the parent folder when a file is detected
  1555. }
  1556. Bool Run(C Str &name, C Str &params, Bool hidden, Bool as_admin)
  1557. {
  1558. if(name.is())
  1559. {
  1560. #if WINDOWS_OLD
  1561. if(SpecialLink(name))return IntPtr(ShellExecute(null, L"open", name , params, null , hidden ? SW_HIDE : SW_SHOWNORMAL))>32;
  1562. return IntPtr(ShellExecute(null, as_admin ? L"runas" : L"open", Str(name).tailSlash(false), params, WChar(_GetPath(name)), hidden ? SW_HIDE : SW_SHOWNORMAL))>32; // always remove tail slash, because if trying to open a local folder "Folder/" with slash at the end then it will fail (however if full path is used, or no tail slash, then it will work)
  1563. #elif WINDOWS_NEW
  1564. if(SpecialLink(name))return Windows::System::Launcher::LaunchUriAsync(ref new Windows::Foundation::Uri(ref new Platform::String(name)))!=null;
  1565. switch(FileInfoSystem(name).type)
  1566. {
  1567. case FSTD_FILE:
  1568. {
  1569. create_task(Windows::Storage::StorageFile::GetFileFromPathAsync(ref new Platform::String(WindowsPath(name)))).then([](task<Windows::Storage::StorageFile^> task) // 'WindowsPath' must be used or exception will occur when using '/' instead of '\'
  1570. {
  1571. try{Windows::System::Launcher::LaunchFileAsync(task.get());} catch(...){}
  1572. });
  1573. }return true;
  1574. case FSTD_DIR:
  1575. {
  1576. create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(ref new Platform::String(WindowsPath(name)))).then([](task<Windows::Storage::StorageFolder^> task) // 'WindowsPath' must be used or exception will occur when using '/' instead of '\'
  1577. {
  1578. try{Windows::System::Launcher::LaunchFolderAsync(task.get());} catch(...){}
  1579. });
  1580. }return true;
  1581. }
  1582. #elif LINUX
  1583. Str8 command; FileInfoSystem fi(name);
  1584. if(SpecialLink(name) || fi.type==FSTD_DRIVE || fi.type==FSTD_DIR)command="xdg-open"; // special links and folders/drives can be opened only with "xdg-open" command
  1585. command.space()+='"'; command+=UnixPathUTF8(name); command+='"';
  1586. if(params.is())command.space()+=UnixPathUTF8(params);
  1587. command+=" &"; // normally 'system' is blocking on Linux, but adding " &" makes the call non-blocking
  1588. return system(command)!=0;
  1589. #elif MAC
  1590. Str8 command; Bool open=false; FileInfoSystem fi(name);
  1591. if(SpecialLink(name) || fi.type==FSTD_DRIVE || fi.type==FSTD_DIR){open=true; command="open"; if(fi.type==FSTD_DIR && Equal(_GetExt(name), "app"))command.space()+="--new"; if(hidden)command.space()+="--hide";} // special links and folders/drives can be opened only with "open" command, use --new for apps so multiple instances can be opened
  1592. command.space()+='"'; command+=UnixPathUTF8(name); command+='"';
  1593. if(params.is()){if(open)command.space()+="--args"; command.space()+=UnixPathUTF8(params);}
  1594. return system(command)!=0;
  1595. #elif IOS
  1596. Bool ok=false;
  1597. //if(SpecialLink(name)) open everything through 'openURL' as there's no other way
  1598. if(NSURLAuto url=name)
  1599. {
  1600. #if 0
  1601. [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; ok=true; // this 'openURL' is async and can't return success, which is now available in 'completionHandler' that will be executed on main thread, since we can't wait, just assume OK
  1602. #else
  1603. ok=[[UIApplication sharedApplication] openURL:url]; // deprecated
  1604. #endif
  1605. }
  1606. return ok;
  1607. #elif ANDROID
  1608. Str path=name;
  1609. if(IsSlash(path[0]) && !Contains(path, ':'))path=S+"file://"+path; // "/sdcard/DCIM/" -> "file:///sdcard/DCIM/"
  1610. JNI jni;
  1611. if(jni && ActivityClass)
  1612. if(JClass uriClass=JClass(jni, "android/net/Uri"))
  1613. if(JClass intentClass=JClass(jni, "android/content/Intent"))
  1614. if(JString urlStr=JString(jni, UnixPathUTF8(path)))
  1615. if(JMethodID parse=jni->GetStaticMethodID(uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"))
  1616. if(JObject uri=JObject(jni, jni->CallStaticObjectMethod(uriClass, parse, urlStr())))
  1617. if(JMethodID intentCtor=jni->GetMethodID(intentClass, "<init>", "(Ljava/lang/String;)V"))
  1618. if(JFieldID actionViewField=jni->GetStaticFieldID(intentClass, "ACTION_VIEW", "Ljava/lang/String;"))
  1619. if(JObject actionView=JObject(jni, jni->GetStaticObjectField(intentClass, actionViewField)))
  1620. if(JObject intent=JObject(jni, jni->NewObject(intentClass, intentCtor, actionView())))
  1621. if(JMethodID startActivity=jni->GetMethodID(ActivityClass, "startActivity", "(Landroid/content/Intent;)V"))
  1622. {
  1623. if(Starts(path, "file:"))
  1624. {
  1625. Str ext=GetExt(path), mime;
  1626. if( ext.is())switch(ExtType(ext))
  1627. {
  1628. case EXT_TEXT : mime= "text/*"; break;
  1629. case EXT_IMAGE: mime="image/*"; break;
  1630. case EXT_VIDEO: mime="video/*"; break;
  1631. case EXT_SOUND: mime="audio/*"; break;
  1632. }
  1633. if(mime.is())
  1634. if(JMethodID setDataAndType=jni->GetMethodID(intentClass, "setDataAndType", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;"))
  1635. if(JString jmime=JString(jni, mime))
  1636. {
  1637. JObject temp=JObject(jni, jni->CallObjectMethod(intent, setDataAndType, uri(), jmime()));
  1638. jni->CallVoidMethod(Activity, startActivity, intent());
  1639. if(!jni->ExceptionCheck())return true; jni->ExceptionClear(); // Java exception "ActivityNotFound" can occur
  1640. }
  1641. }
  1642. if(JMethodID setData=jni->GetMethodID(intentClass, "setData", "(Landroid/net/Uri;)Landroid/content/Intent;"))
  1643. {
  1644. JObject temp=JObject(jni, jni->CallObjectMethod(intent, setData, uri()));
  1645. jni->CallVoidMethod(Activity, startActivity, intent());
  1646. if(!jni->ExceptionCheck())return true; jni->ExceptionClear(); // Java exception "ActivityNotFound" can occur
  1647. }
  1648. }
  1649. #elif WEB
  1650. return JavaScriptRunI(S+"window.open(\""+CString(name)+"\")")!=0;
  1651. #endif
  1652. return false;
  1653. }
  1654. return true;
  1655. }
  1656. /******************************************************************************/
  1657. Bool OpenAppSettings()
  1658. {
  1659. #if IOS
  1660. if(NSURL *url=[NSURL URLWithString:UIApplicationOpenSettingsURLString])
  1661. {
  1662. [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
  1663. //NSURL from 'URLWithString' should not be released as it causes crash
  1664. return true;
  1665. }
  1666. #elif ANDROID
  1667. JNI jni;
  1668. if(jni && ActivityClass)
  1669. if(JMethodID openAppSettings=jni->GetStaticMethodID(ActivityClass, "openAppSettings", "()Z"))
  1670. return jni->CallStaticBooleanMethod(ActivityClass, openAppSettings);
  1671. #endif
  1672. return false;
  1673. }
  1674. /******************************************************************************/
  1675. #if ANDROID
  1676. static CChar8* AndroidPermissions[]=
  1677. {
  1678. "android.permission.WRITE_EXTERNAL_STORAGE", // 0
  1679. "android.permission.ACCESS_FINE_LOCATION" , // 1
  1680. "android.permission.RECORD_AUDIO" , // 2
  1681. "android.permission.GET_ACCOUNTS" , // 3
  1682. };
  1683. ASSERT(PERMISSION_EXTERNAL_STORAGE==0 && PERMISSION_LOCATION==1 && PERMISSION_SOUND_RECORD==2 && PERMISSION_USER_NAME==3 && PERMISSION_NUM==4);
  1684. #endif
  1685. Bool HasPermission(PERMISSION permission)
  1686. {
  1687. #if ANDROID
  1688. if(Activity && InRange(permission, AndroidPermissions))
  1689. {
  1690. JNI jni;
  1691. if(jni)
  1692. if(JClass ContextClass=JClass(jni, "android/content/Context"))
  1693. if(JMethodID checkSelfPermission=jni->GetMethodID(ContextClass, "checkSelfPermission", "(Ljava/lang/String;)I"))
  1694. if(JString t=JString(jni, AndroidPermissions[permission]))
  1695. return jni->CallIntMethod(Activity, checkSelfPermission, t())==0; // PackageManager.PERMISSION_GRANTED=0
  1696. }
  1697. return true; // if permission checking not available, then assume we have it
  1698. #else
  1699. return true;
  1700. #endif
  1701. }
  1702. void GetPermission(PERMISSION permission)
  1703. {
  1704. #if ANDROID
  1705. if(InRange(permission, AndroidPermissions))
  1706. {
  1707. JNI jni;
  1708. if(jni && Activity && ActivityClass)
  1709. if(JMethodID requestPermissions=jni->GetMethodID(ActivityClass, "requestPermissions", "([Ljava/lang/String;I)V"))
  1710. if(JObjectArray permissions=JObjectArray(jni, 1))
  1711. {
  1712. permissions.set(0, AndroidPermissions[permission]);
  1713. jni->CallVoidMethod(Activity, requestPermissions, permissions(), jint(0));
  1714. }
  1715. }
  1716. #endif
  1717. }
  1718. static Int PermissionAsked; ASSERT(PERMISSION_NUM<=32); // each permission is stored in separate bit
  1719. void RequirePermission(PERMISSION permission)
  1720. {
  1721. if(!HasPermission(permission) && !(AtomicOr(PermissionAsked, 1<<permission)&(1<<permission)))GetPermission(permission); // check if haven't asked yet, because on Android 6.0.1 Galaxy Note 4 if selecting "don't ask again" and then "Deny" then app will continuously try to re-initialize itself every frame and eventually crash
  1722. }
  1723. /******************************************************************************/
  1724. #if WEB
  1725. void JavaScriptRun (CChar8 *code) { emscripten_run_script (code);}
  1726. Int JavaScriptRunI(CChar8 *code) {return emscripten_run_script_int (code);}
  1727. CChar8* JavaScriptRunS(CChar8 *code) {return emscripten_run_script_string(code);}
  1728. #else
  1729. void JavaScriptRun (CChar8 *code) {}
  1730. Int JavaScriptRunI(CChar8 *code) {return 0;}
  1731. CChar8* JavaScriptRunS(CChar8 *code) {return null;}
  1732. #endif
  1733. void JavaScriptRun (CChar *code) { JavaScriptRun (UTF8(code));}
  1734. Int JavaScriptRunI(CChar *code) {return JavaScriptRunI(UTF8(code));}
  1735. CChar8* JavaScriptRunS(CChar *code) {return JavaScriptRunS(UTF8(code));}
  1736. /******************************************************************************/
  1737. Bool CreateShortcut(C Str &file, C Str &shortcut, C Str &desc, C Str &icon)
  1738. {
  1739. #if WINDOWS_OLD
  1740. Bool ret=false;
  1741. IShellLink *link; if(OK(CoCreateInstance(CLSID_ShellLink, null, CLSCTX_INPROC_SERVER, IID_IShellLink, (Ptr*)&link)))
  1742. {
  1743. IPersistFile *pf; if(OK(link->QueryInterface(IID_IPersistFile, (Ptr*)&pf)))
  1744. {
  1745. if(OK(link->SetPath ( file ))
  1746. && OK(link->SetWorkingDirectory(WChar(_GetPath(file)))))
  1747. {
  1748. link->SetDescription (desc);
  1749. if(icon.is())link->SetIconLocation(icon, 0);
  1750. ret=OK(pf->Save(shortcut+".lnk", TRUE));
  1751. }
  1752. pf->Release();
  1753. }
  1754. link->Release();
  1755. }
  1756. return ret;
  1757. #elif MAC
  1758. /*#if MAC_OS_X_VERSION_10_6 <= MAC_OS_X_VERSION_MAX_ALLOWED
  1759. require "file://" prefix - which means full path is required
  1760. Bool ok=false;
  1761. if(NSURLAuto src =file)
  1762. if(NSURLAuto dest=shortcut)
  1763. if(NSData *bookmarkData=[src() bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile includingResourceValuesForKeys:nil relativeToURL:nil error:null])
  1764. {
  1765. ok=[NSURL writeBookmarkData:bookmarkData toURL:dest options:0 error:null];
  1766. [bookmarkData release];
  1767. }
  1768. return ok;
  1769. #endif*/
  1770. FSRef src_ref; if(FSPathMakeRef((UInt8*)(UnixPathUTF8(file)()), &src_ref, null)!=noErr)return false;
  1771. FSCatalogInfo catalog_info; if(FSGetCatalogInfo(&src_ref, kFSCatInfoFinderInfo, &catalog_info, null, null, null)!=noErr)return false;
  1772. AppleFileInfo *file_info=(AppleFileInfo*)(&catalog_info.finderInfo);
  1773. OSType file_type=file_info->fileType,
  1774. creator_type=file_info->fileCreator;
  1775. IconRef icon_ref; GetIconRefFromFileInfo(&src_ref, 0, null, kFSCatInfoFinderInfo, &catalog_info, kIconServicesNormalUsageFlag, &icon_ref, null);
  1776. IconFamilyHandle icon_family; IconRefToIconFamily(icon_ref, kSelectorAllAvailableData, &icon_family);
  1777. AliasHandle alias; FSNewAliasMinimal(&src_ref, &alias); if(!alias)return false;
  1778. FSRef parent_ref; if(FSPathMakeRef((UInt8*)(UnixPathUTF8(GetPath(shortcut))()), &parent_ref, null)!=noErr)return false;
  1779. Char alias_name[256]; Set(alias_name, GetBase(shortcut));
  1780. FSRef alias_ref; FSCreateResFile(&parent_ref, Length(alias_name), (UniChar*)alias_name, 0, null, &alias_ref, null); if(!FSIsFSRefValid(&alias_ref))return false;
  1781. // create resource
  1782. ResFileRefNum file_ref=FSOpenResFile(&alias_ref, fsRdWrPerm);
  1783. UseResFile (file_ref);
  1784. AddResource ((Handle)alias, 'alis', 0, null);
  1785. AddResource ((Handle)icon_family, kIconFamilyType, kCustomIconResource, "\p");
  1786. CloseResFile(file_ref);
  1787. // adjust alias
  1788. if(FSGetCatalogInfo(&alias_ref, kFSCatInfoFinderInfo, &catalog_info, null, null, null)!=noErr)return false;
  1789. file_info=(AppleFileInfo*)(&catalog_info.finderInfo);
  1790. file_info->finderFlags|= kIsAlias|kHasCustomIcon;
  1791. file_info->finderFlags&=~kHasBeenInited;
  1792. file_info->fileType = file_type;
  1793. file_info->fileCreator = creator_type;
  1794. if(FSSetCatalogInfo(&alias_ref, kFSCatInfoFinderInfo, &catalog_info)!=noErr)return false;
  1795. return true;
  1796. #elif LINUX
  1797. FileText ft; if(ft.write(shortcut+".desktop", UTF_8_NAKED)) // Linux will fail if BOM is present
  1798. {
  1799. ft.putLine("[Desktop Entry]");
  1800. ft.putLine("Encoding=UTF-8");
  1801. ft.putLine("Type=Application");
  1802. ft.putLine("Terminal=false");
  1803. ft.putLine(S+"Name="+GetBaseNoExt(shortcut));
  1804. if(desc.is())ft.putLine(S+"GenericName="+ Replace(desc, '\n', '\0') );
  1805. if(icon.is())ft.putLine(S+"Icon=\"" +UnixPath(Replace(icon, '\n', '\0'))+'"');
  1806. if(file.is())ft.putLine(S+"Exec=\"" +UnixPath(Replace(file, '\n', '\0'))+'"');
  1807. return true;
  1808. }
  1809. #endif
  1810. return false;
  1811. }
  1812. /******************************************************************************/
  1813. Bool AssociateFileType(Str extension, Str application_path, Str application_id, Str extension_desc, Str custom_icon)
  1814. {
  1815. Bool ok=false;
  1816. if(extension[0]=='.')extension.remove(0); // ".ext" -> "ext"
  1817. if(extension.is() && application_path.is() && application_id.is() && application_id[0]!='.')
  1818. {
  1819. #if WINDOWS_OLD
  1820. extension =S+'.'+extension;
  1821. application_id +=extension;
  1822. application_path=S+'"'+Replace(application_path, '/', '\\')+"\" \"%1\"";
  1823. custom_icon =Replace(custom_icon, '/', '\\');
  1824. ok=true;
  1825. Bool changed=false;
  1826. Str name=S+"Software/Classes/"+application_id+"/shell/open/command/"; if(!Equal(GetRegStr(RKG_LOCAL_MACHINE, name), application_path, true))if(SetRegStr(RKG_LOCAL_MACHINE, name, application_path))changed=true;else ok=false;
  1827. name=S+"Software/Classes/"+extension +"/" ; if(!Equal(GetRegStr(RKG_LOCAL_MACHINE, name), application_id , true))if(SetRegStr(RKG_LOCAL_MACHINE, name, application_id ))changed=true;else ok=false;
  1828. name=S+"Software/Classes/"+application_id+"/" ; if(!Equal(GetRegStr(RKG_LOCAL_MACHINE, name), extension_desc , true))if(SetRegStr(RKG_LOCAL_MACHINE, name, extension_desc ))changed=true; // optional
  1829. name=S+"Software/Classes/"+application_id+"/DefaultIcon/" ; if(!Equal(GetRegStr(RKG_LOCAL_MACHINE, name), custom_icon , true))if(SetRegStr(RKG_LOCAL_MACHINE, name, custom_icon ))changed=true; // optional
  1830. /*if(ok) // some more advanced stuff for vista and newer
  1831. {
  1832. IApplicationAssociationRegistration *aar=null;
  1833. CoCreateInstance(CLSID_ApplicationAssociationRegistration, null, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration), (Ptr*)&aar);
  1834. if(aar)
  1835. {
  1836. ok=OK(aar->SetAppAsDefault(application_id, extension, AT_FILEEXTENSION));
  1837. aar->Release();
  1838. }
  1839. }*/
  1840. if(ok && changed)SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, null, null);
  1841. #endif
  1842. }
  1843. return ok;
  1844. }
  1845. /******************************************************************************/
  1846. EXTENSION_TYPE ExtType(C Str &ext)
  1847. {
  1848. if(ext=="txt" || ext=="xml" || ext=="htm" || ext=="html" || ext=="php" || ext=="cpp" || ext=="c" || ext=="h" || ext=="java" || ext=="cs" || ext=="m" || ext=="mm" || ext=="cxx" || ext=="cc" || ext=="mk" )return EXT_TEXT;
  1849. if(ext=="bmp" || ext=="jpg" || ext=="jpeg" || ext=="png" || ext=="tga" || ext=="tif" || ext=="tiff" || ext=="dds" || ext=="psd" || ext=="gif" || ext=="ico" || ext=="cur" || ext=="icns" || ext=="webp" || ext=="bpg" )return EXT_IMAGE;
  1850. if(ext=="mp3" || ext=="ogg" || ext=="wma" || ext=="wav" || ext=="m4a" || ext=="flac" || ext=="opus" || ext=="weba" )return EXT_SOUND;
  1851. if(ext=="3ds" || ext=="ase" || ext=="obj" || ext=="ms3d" || ext=="b3d" || ext=="dae" || ext=="fbx" || ext=="psk" || ext=="psa" || ext=="bvh" )return EXT_MESH;
  1852. if(ext=="avi" || ext=="mpg" || ext=="mpeg" || ext=="mp4" || ext=="m4v" || ext=="mkv" || ext=="wmv" || ext=="rmvb" || ext=="divx" || ext=="ogm" || ext=="ogv" || ext=="webm" || ext=="vob" || ext=="flv" || ext=="theora")return EXT_VIDEO;
  1853. // Esenthel formats
  1854. if(ext=="img" )return EXT_IMAGE;
  1855. if(ext=="mesh")return EXT_MESH;
  1856. //if(ext=="mtrl")return EXT_MATERIAL;
  1857. //if(ext=="phys")return EXT_PHYS;
  1858. return EXT_NONE;
  1859. }
  1860. /******************************************************************************/
  1861. Bool HasDrive(CChar *path)
  1862. {
  1863. if(path)
  1864. {
  1865. if(((path[0]>='a' && path[0]<='z') || (path[0]>='A' && path[0]<='Z')) && path[1]==':')return true; // C: Windows Style
  1866. if(IsSlash(path[0]))return true; // / Unix style
  1867. }
  1868. return false;
  1869. }
  1870. Bool HasDrive(CChar8 *path)
  1871. {
  1872. if(path)
  1873. {
  1874. if(((path[0]>='a' && path[0]<='z') || (path[0]>='A' && path[0]<='Z')) && path[1]==':')return true; // C: Windows Style
  1875. if(IsSlash(path[0]))return true; // / Unix style
  1876. }
  1877. return false;
  1878. }
  1879. Bool IsDrive(CChar *path)
  1880. {
  1881. if(path)
  1882. {
  1883. if(((path[0]>='a' && path[0]<='z') || (path[0]>='A' && path[0]<='Z')) && path[1]==':' && (!path[2] || (IsSlash(path[2]) && !path[3])))return true; // C: or C:\ Windows Style
  1884. if(IsSlash(path[0])) // on Unix we can also have "/Volumes/.."
  1885. {
  1886. if(!path[1])return true;
  1887. if(Starts(path+1, "Volumes") && IsSlash(path[8]) && (path[9] && !IsSlash(path[9])))for(path+=10; ; )
  1888. {
  1889. Char c=*path++;
  1890. if( !c )return true;
  1891. if(IsSlash(c))return (*path)==0;
  1892. }
  1893. }
  1894. }
  1895. return false;
  1896. }
  1897. Bool IsDrive(CChar8 *path)
  1898. {
  1899. if(path)
  1900. {
  1901. if(((path[0]>='a' && path[0]<='z') || (path[0]>='A' && path[0]<='Z')) && path[1]==':' && (!path[2] || (IsSlash(path[2]) && !path[3])))return true; // C: or C:\ Windows Style
  1902. if(IsSlash(path[0])) // on Unix we can also have "/Volumes/.."
  1903. {
  1904. if(!path[1])return true;
  1905. if(Starts(path+1, "Volumes") && IsSlash(path[8]) && (path[9] && !IsSlash(path[9])))for(path+=10; ; )
  1906. {
  1907. Char c=*path++;
  1908. if( !c )return true;
  1909. if(IsSlash(c))return (*path)==0;
  1910. }
  1911. }
  1912. }
  1913. return false;
  1914. }
  1915. Bool HasRemote(CChar *path) {return path && path[0]=='\\' && path[1]=='\\';}
  1916. Bool HasRemote(CChar8 *path) {return path && path[0]=='\\' && path[1]=='\\';}
  1917. Bool FullPath (CChar *path) {return HasDrive(path) || HasRemote(path);}
  1918. Bool FullPath (CChar8 *path) {return HasDrive(path) || HasRemote(path);}
  1919. /******************************************************************************/
  1920. // GET
  1921. /******************************************************************************/
  1922. // TODO: all of these impose limits which could break some functionalities requiring very long strings
  1923. CChar* _GetBase(CChar *name, Bool tail_slash, Char (&dest)[MAX_LONG_PATH])
  1924. {
  1925. if(name)
  1926. {
  1927. if(IsDrive(name))
  1928. {
  1929. if(!tail_slash) // if want to remove tail slash
  1930. {
  1931. if(Int length=Length(name))if(IsSlash(name[length-1]))
  1932. {
  1933. if(length=SetReturnLength(dest, name))if(IsSlash(dest[length-1]))dest[--length]=0;
  1934. return dest;
  1935. }
  1936. }
  1937. return name;
  1938. }
  1939. if(Int length=Length(name))
  1940. {
  1941. Bool ends_with_slash=IsSlash(name[length-1]); if(ends_with_slash)length--;
  1942. for(; length--; )if(IsSlash(name[length]))break;
  1943. if(!tail_slash && ends_with_slash) // if want to remove tail slash and ends with one then we need to copy to 'dest'
  1944. {
  1945. if(length=SetReturnLength(dest, name+length+1))if(IsSlash(dest[length-1]))dest[--length]=0;
  1946. return dest;
  1947. }else
  1948. {
  1949. return name+length+1;
  1950. }
  1951. }
  1952. }
  1953. return null;
  1954. }
  1955. /******************************************************************************/
  1956. CChar* _GetBaseNoExt(CChar *name, Char (&dest)[MAX_LONG_PATH])
  1957. {
  1958. if(name)
  1959. {
  1960. if(IsDrive(name))
  1961. {
  1962. if(Int length=Length(name))if(IsSlash(name[length-1]))
  1963. {
  1964. if(length=SetReturnLength(dest, name))if(IsSlash(dest[length-1]))dest[--length]=0;
  1965. return dest;
  1966. }
  1967. return name;
  1968. }
  1969. if(Int length=Length(name))
  1970. {
  1971. Bool ends_with_slash=IsSlash(name[length-1]); if(ends_with_slash)length--;
  1972. for(; length--; )if(IsSlash(name[length]))break;
  1973. if(length=SetReturnLength(dest, name+length+1))
  1974. {
  1975. if(IsSlash(dest[length-1]))dest[--length]=0;
  1976. for(; length--; )if(dest[length]=='.'){dest[length]=0; break;}
  1977. }
  1978. return dest;
  1979. }
  1980. }
  1981. return null;
  1982. }
  1983. /******************************************************************************/
  1984. CChar* _GetExt(CChar *name, Char (&dest)[MAX_LONG_PATH])
  1985. {
  1986. if(Int length=Length(name))
  1987. {
  1988. if(IsSlash(name[length-1])) // if ends with slash then we need to copy to 'dest'
  1989. {
  1990. for(length--; length--; )
  1991. {
  1992. Char c=name[length];
  1993. if(IsSlash(c))break;
  1994. if(c=='.')
  1995. {
  1996. if(length=SetReturnLength(dest, name+length+1))if(IsSlash(dest[length-1]))dest[--length]=0;
  1997. return dest;
  1998. }
  1999. }
  2000. }else for(; length--; )
  2001. {
  2002. Char c=name[length];
  2003. if(IsSlash(c))break;
  2004. if(c=='.')return name+length+1;
  2005. }
  2006. }
  2007. return null;
  2008. }
  2009. /******************************************************************************/
  2010. CChar* _GetExtNot(CChar *name, Char (&dest)[MAX_LONG_PATH])
  2011. {
  2012. if(Int length=SetReturnLength(dest, name))
  2013. {
  2014. if(IsSlash(dest[length-1]))dest[--length]=0;
  2015. for(; length--; )
  2016. {
  2017. Char c=dest[length];
  2018. if(IsSlash(c))break;
  2019. if(c=='.'){dest[length]=0; break;}
  2020. }
  2021. }
  2022. return dest;
  2023. }
  2024. /******************************************************************************/
  2025. CChar* _GetPath(CChar *name, Int tail_slash, Char (&dest)[MAX_LONG_PATH])
  2026. {
  2027. if(!IsDrive(name))
  2028. if(Int length=SetReturnLength(dest, name))
  2029. {
  2030. Bool ends_with_slash=IsSlash(dest[length-1]); if(ends_with_slash)length--;
  2031. Bool insert_tail_slash=((tail_slash<0) ? ends_with_slash : tail_slash!=0);
  2032. for(; length--; )if(IsSlash(dest[length]))
  2033. {
  2034. if(length<=1 && HasRemote(dest)) // reached remote "\\*" or "\\"
  2035. {
  2036. if(!dest[2])break; // if there's nothing after remote, then always return empty string
  2037. dest[2]=0; // return "\\"
  2038. }else
  2039. if(length)dest[length+insert_tail_slash]=0;else dest[1]=0;
  2040. return dest;
  2041. }
  2042. }
  2043. dest[0]=0; return dest;
  2044. }
  2045. /******************************************************************************/
  2046. Char8* _GetStart(CChar8 *name, Char8 (&dest)[MAX_LONG_PATH]) // this function always returns 'dest' so we can use 'Char8' instead of 'CChar8' in case we want to modify it
  2047. {
  2048. Int i=0;
  2049. if(Is(name))for(; i<Elms(dest)-1; i++)
  2050. {
  2051. Char8 c=*name++; if(!c || IsSlash(c))break;
  2052. dest[i]=c;
  2053. }
  2054. dest[i]=0; return dest;
  2055. }
  2056. Char* _GetStart(CChar *name, Char (&dest)[MAX_LONG_PATH]) // this function always returns 'dest' so we can use 'Char8' instead of 'CChar8' in case we want to modify it
  2057. {
  2058. Int i=0;
  2059. if(Is(name))for(; i<Elms(dest)-1; i++)
  2060. {
  2061. Char c=*name++; if(!c || IsSlash(c))break;
  2062. dest[i]=c;
  2063. }
  2064. dest[i]=0; return dest;
  2065. }
  2066. /******************************************************************************/
  2067. CChar* _GetStartNot(CChar *name)
  2068. {
  2069. if(Is(name))for(;;)
  2070. {
  2071. Char c=*name++; if(!c)return null;
  2072. if(IsSlash(c))return name;
  2073. }
  2074. return null;
  2075. }
  2076. /******************************************************************************/
  2077. Str GetBase (C Str &name) {return _GetBase (name);}
  2078. Str GetBaseNoExt(C Str &name) {return _GetBaseNoExt(name);}
  2079. Str GetExt (C Str &name) {return _GetExt (name);}
  2080. Str GetExtNot (C Str &name) {return _GetExtNot (name);}
  2081. Str GetPath (C Str &name) {return _GetPath (name);}
  2082. Str GetStart (C Str &name) {return _GetStart (name);}
  2083. Str GetStartNot (C Str &name) {return _GetStartNot (name);}
  2084. /******************************************************************************/
  2085. Str GetRelativePath(Str src, Str dest)
  2086. {
  2087. if(FullPath(dest) || FullPath(src)) // if any path is full path
  2088. if(GetStart(dest)!=GetStart(src)) // not on the same drive
  2089. return dest; // need to return full path
  2090. // eat common path
  2091. for(;;)
  2092. {
  2093. Str start=GetStart(dest);
  2094. if( start.length()==dest.length() || start!=GetStart(src))break;
  2095. dest=GetStartNot(dest);
  2096. src =GetStartNot(src );
  2097. }
  2098. for(; src.is(); )
  2099. {
  2100. src =GetPath(src);
  2101. dest.insert(0, "..\\"); // dest=S+"..\\"+dest;
  2102. }
  2103. return dest;
  2104. }
  2105. /******************************************************************************/
  2106. #if WINDOWS_OLD
  2107. static const KNOWNFOLDERID FOLDERID_OneDrive ={0xA52BBA46, 0xE9E1, 0x435f, {0xB3, 0xD9, 0x28, 0xDA, 0xA6, 0x48, 0xC0, 0xF6}},
  2108. FOLDERID_SavedGames={0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}};
  2109. static Str GetKnownFolderPath(C KNOWNFOLDERID &folder_id)
  2110. {
  2111. #if SUPPORT_WINDOWS_XP
  2112. #define KF_FLAG_CREATE 0x00008000
  2113. #define KF_FLAG_NO_ALIAS 0x00001000
  2114. if(HMODULE shell=GetModuleHandle(L"Shell32.dll"))
  2115. if(HRESULT (STDAPICALLTYPE *SHGetKnownFolderPath)(REFKNOWNFOLDERID rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath)=(decltype(SHGetKnownFolderPath))GetProcAddress(shell, "SHGetKnownFolderPath")) // available on Vista+
  2116. #endif
  2117. {
  2118. ASSERT( KF_FLAG_CREATE ==0x00008000
  2119. && KF_FLAG_NO_ALIAS==0x00001000);
  2120. PWSTR sg_path=null; SHGetKnownFolderPath(folder_id, KF_FLAG_CREATE|KF_FLAG_NO_ALIAS, null, &sg_path);
  2121. if( sg_path)
  2122. {
  2123. Str path=sg_path; CoTaskMemFree(sg_path);
  2124. return path;
  2125. }
  2126. }
  2127. return S;
  2128. }
  2129. #endif
  2130. Str SystemPath(SYSTEM_PATH type)
  2131. {
  2132. #if WINDOWS_OLD
  2133. wchar_t path[MAX_PATH]; path[0]=0; // 'SHGetFolderPath' requires MAX_PATH
  2134. UInt out;
  2135. switch(type)
  2136. {
  2137. case SP_ONE_DRIVE: return GetKnownFolderPath(FOLDERID_OneDrive);
  2138. case SP_SAVED_GAMES:
  2139. {
  2140. Str path=GetKnownFolderPath(FOLDERID_SavedGames); if(path.is())return path; // if unknown then fallback to SP_DOCUMENTS
  2141. } // !! no break on purpose !!
  2142. case SP_DOCUMENTS : out=CSIDL_MYDOCUMENTS ; break;
  2143. case SP_DESKTOP : out=CSIDL_DESKTOPDIRECTORY; break;
  2144. case SP_PROG_FILES : out=CSIDL_PROGRAM_FILES ; break;
  2145. case SP_MENU : out=CSIDL_STARTMENU ; break;
  2146. case SP_MENU_PROG : out=CSIDL_PROGRAMS ; break;
  2147. case SP_STARTUP : out=CSIDL_STARTUP ; break;
  2148. case SP_APP_DATA : out=CSIDL_APPDATA ; break;
  2149. case SP_APP_DATA_PUBLIC: out=CSIDL_APPDATA ; break;
  2150. case SP_ALL_APP_DATA : out=CSIDL_COMMON_APPDATA ; break;
  2151. case SP_FAVORITES : out=CSIDL_FAVORITES ; break;
  2152. case SP_SYSTEM : out=CSIDL_SYSTEM ; break;
  2153. default : return S;
  2154. }
  2155. SHGetFolderPath(0, out, 0, 0, path); ASSERT(Elms(path)==MAX_PATH); // !! SHGetFolderPath requires MAX_PATH !!
  2156. if(type==SP_SAVED_GAMES && path[0])Append(WChar(path), "\\My Games", Elms(path));
  2157. return path;
  2158. #elif WINDOWS_NEW
  2159. Windows::Storage::StorageFolder ^folder;
  2160. switch(type)
  2161. {
  2162. case SP_APP_DATA :
  2163. case SP_APP_DATA_PUBLIC:
  2164. case SP_SAVED_GAMES : folder=Windows::Storage::ApplicationData::Current->LocalFolder; break;
  2165. case SP_DOCUMENTS: folder=Windows::Storage::KnownFolders::DocumentsLibrary; break;
  2166. }
  2167. if(folder)return folder->Path->Data();
  2168. #elif LINUX
  2169. Char8 path[MAX_UTF_PATH]; path[0]=0;
  2170. switch(type)
  2171. {
  2172. case SP_DESKTOP : MergePath(path, getenv("HOME"), "Desktop" ); break;
  2173. case SP_DOCUMENTS : MergePath(path, getenv("HOME"), "Documents"); break;
  2174. case SP_SAVED_GAMES :
  2175. case SP_APP_DATA :
  2176. case SP_APP_DATA_PUBLIC: Set(path, getenv("HOME")); break;
  2177. case SP_ALL_APP_DATA : return "/home";
  2178. case SP_TRASH:
  2179. {
  2180. if(char *XDG_DATA_HOME=getenv("XDG_DATA_HOME"))MergePath(path, XDG_DATA_HOME, "Trash");else
  2181. if(char * HOME=getenv( "HOME"))MergePath(path, HOME, ".local\\share\\Trash");
  2182. }break;
  2183. }
  2184. return Replace(FromUTF8(path), '/', '\\');
  2185. #elif MAC
  2186. OSType out;
  2187. switch(type)
  2188. {
  2189. case SP_DESKTOP : out=kDesktopFolderType ; break;
  2190. case SP_PROG_FILES : out=kApplicationsFolderType ; break;
  2191. case SP_SAVED_GAMES : out=kApplicationSupportFolderType; break;
  2192. case SP_APP_DATA : out=kApplicationSupportFolderType; break;
  2193. case SP_APP_DATA_PUBLIC: out=kApplicationSupportFolderType; break;
  2194. case SP_DOCUMENTS : out=kDocumentsFolderType ; break;
  2195. case SP_FAVORITES : out=kFavoritesFolderType ; break;
  2196. case SP_FRAMEWORKS : out=kFrameworksFolderType ; break;
  2197. default : return S;
  2198. }
  2199. Char8 path[MAX_UTF_PATH]; path[0]=0;
  2200. FSRef fs;
  2201. if(FSFindFolder(kUserDomain, out, true, &fs)==noErr)
  2202. if(CFURLRef url=CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &fs))
  2203. {
  2204. CFURLGetFileSystemRepresentation(url, true, (UInt8*)path, Elms(path));
  2205. CFRelease(url);
  2206. }
  2207. return Replace(FromUTF8(path), '/', '\\');
  2208. #elif IOS
  2209. NSSearchPathDirectory out;
  2210. switch(type)
  2211. {
  2212. case SP_DESKTOP : out=NSDesktopDirectory ; break;
  2213. case SP_DOCUMENTS : out=NSDocumentDirectory ; break;
  2214. case SP_SAVED_GAMES : out=NSDocumentDirectory ; break;
  2215. case SP_APP_DATA : out=NSDocumentDirectory ; break;
  2216. case SP_APP_DATA_PUBLIC: out=NSDocumentDirectory ; break;
  2217. case SP_PROG_FILES : out=NSApplicationDirectory; break;
  2218. default : return S;
  2219. }
  2220. if(NSFileManager *fileManager=[NSFileManager defaultManager])
  2221. if(NSArray *paths=NSSearchPathForDirectoriesInDomains(out, NSUserDomainMask, YES))
  2222. if([paths count])return Replace([paths objectAtIndex:0], '/', '\\'); // don't release anything as it causes bad access
  2223. #elif ANDROID
  2224. switch(type)
  2225. {
  2226. case SP_DOCUMENTS : return AndroidAppDataPath;
  2227. case SP_SAVED_GAMES : return AndroidAppDataPublicPath.is() ? AndroidAppDataPublicPath : AndroidAppDataPath;
  2228. case SP_APP_DATA : return AndroidAppDataPath;
  2229. case SP_APP_DATA_PUBLIC: return AndroidAppDataPublicPath;
  2230. case SP_PUBLIC : return AndroidPublicPath;
  2231. case SP_SD_CARD : return AndroidSDCardPath;
  2232. }
  2233. #elif WEB
  2234. switch(type)
  2235. {
  2236. case SP_DOCUMENTS :
  2237. case SP_SAVED_GAMES :
  2238. case SP_APP_DATA :
  2239. case SP_APP_DATA_PUBLIC: return "/data";
  2240. }
  2241. #endif
  2242. return S;
  2243. }
  2244. /******************************************************************************/
  2245. Str AndroidExpansionFileName(Int version, Bool main)
  2246. {
  2247. #if ANDROID
  2248. if(AndroidPublicPath.is() && AndroidPackageName.is())
  2249. return AndroidPublicPath+"/Android/obb/"+AndroidPackageName+'/'+(main ? "main" : "patch")+'.'+version+'.'+AndroidPackageName+".obb";
  2250. #endif
  2251. return S;
  2252. }
  2253. /******************************************************************************/
  2254. Str NormalizePath(C Str &path)
  2255. {
  2256. Str s;
  2257. if(CChar *p=path)for(; p[0]; )
  2258. {
  2259. if(p[0]=='.' && p[1]=='.' && (IsSlash(p[2]) || p[2]=='\0') // if starts from going back
  2260. && s.length()>=2) // is not Unix root "/../" and is not empty "../"
  2261. {
  2262. s.removeLast(); // remove last '/' or '\'
  2263. for(; s.is() && !IsSlash(s.last()); )s.removeLast(); // remove last path part
  2264. p+=2; if(*p)p++; // skip .. and / if there's one
  2265. }else
  2266. for(;;) // default name
  2267. {
  2268. Char c=*p++; if(!c )goto end;
  2269. s+=c; if(IsSlash(c))break;
  2270. }
  2271. }
  2272. end:;
  2273. return s;
  2274. }
  2275. /******************************************************************************/
  2276. Str CleanFileName(C Str &name)
  2277. {
  2278. Str temp; temp.reserve(name.length());
  2279. FREPA(name)switch(Char c=name[i]) // skip disallowed characters
  2280. {
  2281. case '/' :
  2282. case '\\':
  2283. case ':' :
  2284. case '*' :
  2285. case '?' :
  2286. case '"' :
  2287. case '<' :
  2288. case '>' :
  2289. case '|' : break;
  2290. default : if(U16(c)>31)temp+=c; break;
  2291. }
  2292. temp.removeOuterWhiteChars(); // on Windows names can't end with spaces (but they can start with spaces), on Windows names can start with spaces (but remove them anyway, because it's a non-standard naming convention to start with a space)
  2293. REPA(temp)if(temp[i]!='.' && temp[i]!=' ')return temp; // on Windows name must contain at least one character which is not space or dot (".", "..", ". .", "......", ". . ." - all of these are invalid names)
  2294. return S;
  2295. }
  2296. /******************************************************************************/
  2297. // DO NOT CHANGE !!
  2298. static const Char8 CleanFileNameArray[]={'!', '#', '^', '-', '_', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
  2299. static const Byte CleanFileNameIndex[]={255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, 255, 255, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255, 255, 255, 255, 255, 255, 255, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 255, 255, 255, 2, 4, 255, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; ASSERT(Elms(CleanFileNameIndex)==256);
  2300. static constexpr Int CleanFileNameElms =Elms(CleanFileNameArray);
  2301. /******************************************************************************
  2302. static Int CompareChar(C Char8 &a, C Char8 &b) {return EE::Compare(a, b);}
  2303. void MakeCleanFileNameArray()
  2304. {
  2305. Str s;
  2306. #if 1 // 41 chars, this is enough to store 6-chars per 4-bytes
  2307. s+="!#^-_";
  2308. for(Int i='a'; i<='z'; i++)s+=Char8(i);
  2309. for(Int i='0'; i<='9'; i++)s+=Char8(i);
  2310. #else // 57 chars
  2311. for(Int i='a'; i<='z'; i++)s+=Char8(i);
  2312. for(Int i='0'; i<='9'; i++)s+=Char8(i);
  2313. s+=" `~!@#$%^&*()_-+=[]{};'\\:\"|,./<>?";
  2314. s.replace(' ', '\0'); // skip space
  2315. s.replace('.', '\0'); // skip . to avoid setting extensions
  2316. s.replace('`', '\0'); // skip ` because it's easy to mix up with '
  2317. #endif
  2318. s=CleanFileName(s);
  2319. Memc<Char8> cs; FREPA(s)cs.include(s[i]); cs.sort(CompareChar);
  2320. s.clear(); FREPA(cs){if(s.is())s+=", "; s+='\''; if(cs[i]=='\'' || cs[i]=='\\')s+='\\'; s+=cs[i]; s+='\'';}
  2321. s.line();
  2322. for(Int i=0; i<256; i++){if(s.is() && s.last()!='\n')s+=", "; s+=Byte(cs.find(CaseDown(Char8(i))));} // use Byte cast so -1 -> 255
  2323. s.line();
  2324. ClipSet(s);
  2325. }
  2326. void Test()
  2327. {
  2328. REPA(CleanFileNameArray)DEBUG_ASSERT(CleanFileNameIndex[(Byte)CleanFileNameArray[i]]==i, "err");
  2329. }
  2330. /******************************************************************************/
  2331. CChar* _EncodeFileName(C UID &id, Char (&name)[24+1]) // 24 chars needed + nul terminate
  2332. {
  2333. Int n=0;
  2334. C UInt *src=id.i;
  2335. REP(SIZE(id)/4)
  2336. {
  2337. ASSERT(CleanFileNameElms==41); // this requires 6 steps per UInt
  2338. UInt u=*src++; REPD(step, 6)
  2339. {
  2340. name[n++]=CleanFileNameArray[u%CleanFileNameElms]; u/=CleanFileNameElms;
  2341. }
  2342. }
  2343. name[n]='\0';
  2344. return name;
  2345. }
  2346. Bool DecodeFileName(CChar *src, UID &id)
  2347. {
  2348. if(src)
  2349. {
  2350. UInt *d=id.i; REP(SIZE(id)/4)
  2351. {
  2352. ASSERT(CleanFileNameElms==41); // this requires 6 steps per UInt
  2353. UInt u=0, mul=1; REPD(step, 6)
  2354. {
  2355. UShort c=*src++ ; if(!InRange(c, CleanFileNameIndex)){invalid_char: *d++=u; for(; --i>=0; )*d++=0; return false;} // clear remaining data
  2356. Byte v=CleanFileNameIndex[c]; if(!InRange(v, CleanFileNameElms ))goto invalid_char;
  2357. u+=v*mul; mul*=CleanFileNameElms;
  2358. }
  2359. *d++=u;
  2360. }
  2361. if(!*src)return true;
  2362. }else id.zero();
  2363. return false;
  2364. }
  2365. /******************************************************************************/
  2366. Str EncodeFileName( CPtr src, Int size) {Str dest; EncodeFileName(dest, src, size); return dest;}
  2367. void EncodeFileName(Str &dest, CPtr src, Int size)
  2368. {
  2369. dest.clear();
  2370. if(Byte *s=(Byte*)src)if(size>0)
  2371. {
  2372. dest.reserve(DivCeil4(size)*6); // 6 chars per 4 bytes
  2373. UInt u, max;
  2374. REP(UInt(size)/4)
  2375. {
  2376. for(u=*(UInt*)s, max=UINT_MAX; ; ){dest.alwaysAppend(CleanFileNameArray[u%CleanFileNameElms]); max/=CleanFileNameElms; if(!max)break; u/=CleanFileNameElms;}
  2377. s+=4;
  2378. }
  2379. switch(size&3)
  2380. {
  2381. case 1: u=* s ; max=0x0000FF; break;
  2382. case 2: u=*(U16*)s ; max=0x00FFFF; break;
  2383. case 3: u=*(U16*)s|(s[2]<<16); max=0xFFFFFF; break;
  2384. default: goto end;
  2385. }
  2386. for(;;){dest.alwaysAppend(CleanFileNameArray[u%CleanFileNameElms]); max/=CleanFileNameElms; if(!max)break; u/=CleanFileNameElms;}
  2387. end:;
  2388. }
  2389. }
  2390. Bool DecodeFileName(C Str &src, Ptr dest, Int size)
  2391. {
  2392. if(Byte *d=(Byte*)dest)if(size>0)
  2393. {
  2394. UInt src_pos=0, u, max, mul;
  2395. for(; size>=4; )
  2396. {
  2397. u=0; max=UINT_MAX; mul=1; for(;;)
  2398. {
  2399. U16 c= src[src_pos++]; if(!InRange(c, CleanFileNameIndex)){invalid_char: *(UInt*)d=u; d+=4; size-=4; REP(size)*d++=0; return false;}
  2400. Byte i=CleanFileNameIndex[c]; if(!InRange(i, CleanFileNameElms ))goto invalid_char;
  2401. u+=i*mul; max/=CleanFileNameElms; if(!max)break; mul*=CleanFileNameElms;
  2402. }
  2403. *(UInt*)d=u; d+=4; size-=4;
  2404. }
  2405. Bool error=false; switch(size)
  2406. {
  2407. case 1: max=0x0000FF; break;
  2408. case 2: max=0x00FFFF; break;
  2409. case 3: max=0xFFFFFF; break;
  2410. default: goto end;
  2411. }
  2412. u=0; mul=1; for(;;)
  2413. {
  2414. U16 c= src[src_pos++]; if(!InRange(c, CleanFileNameIndex)){error=true; break;}
  2415. Byte i=CleanFileNameIndex[c]; if(!InRange(i, CleanFileNameElms )){error=true; break;}
  2416. u+=i*mul; max/=CleanFileNameElms; if(!max)break; mul*=CleanFileNameElms;
  2417. }
  2418. switch(size)
  2419. {
  2420. case 1: * d=u ; break;
  2421. case 2: *(U16*)d=u ; break;
  2422. case 3: *(U16*)d=u&0xFFFF; d[2]=(u>>16); break;
  2423. }
  2424. end:;
  2425. if(error || src.length()!=src_pos)return false; // if there are still characters left then it means that the string is bigger than expected, leave contents on fail
  2426. }
  2427. return true;
  2428. }
  2429. /******************************************************************************/
  2430. Str EncodeRaw( CPtr src, Int size) {Str dest; EncodeRaw(dest, src, size); return dest;}
  2431. void EncodeRaw(Str8 &dest, CPtr src, Int size)
  2432. {
  2433. dest.clear(); // always 'clear' even for 'reserve' to avoid copying old data in 'setNum'
  2434. if(Byte *s=(Byte*)src)if(size>0)
  2435. {
  2436. dest.reserve(size);
  2437. CopyFast(dest._d.data(), src, size);
  2438. dest._d[dest._length=size]='\0'; // set ending zero
  2439. }
  2440. }
  2441. void EncodeRaw(Str &dest, CPtr src, Int size)
  2442. {
  2443. dest.clear(); // always 'clear' even for 'reserve' to avoid copying old data in 'setNum'
  2444. if(Byte *s=(Byte*)src)if(size>0)
  2445. {
  2446. Int length=Ceil2(size)/2; // align to full words
  2447. dest.reserve(length);
  2448. CopyFast(dest._d.data(), src, size);
  2449. if(size&1)((Byte*)dest._d.data())[size]=0; // Char can hold 2 bytes, but if we don't set that Char fully from source, then clear last byte to zero, yes this should be "[size]" and not "[size-1]" (because we've copied 'size' bytes but still have 1 left in last Char)
  2450. dest._d[dest._length=length]='\0'; // set ending zero
  2451. }
  2452. }
  2453. Bool DecodeRaw(C Str8 &src, Ptr dest, Int size)
  2454. {
  2455. if(src.length()!=size)return false;
  2456. if(Byte *d=(Byte*)dest)CopyFast(dest, src(), size);
  2457. return true;
  2458. }
  2459. Bool DecodeRaw(C Str &src, Ptr dest, Int size)
  2460. {
  2461. Int length=Ceil2(size)/2; // align to full words
  2462. if(src.length()!=length)return false;
  2463. if(Byte *d=(Byte*)dest)CopyFast(dest, src(), size);
  2464. return true;
  2465. }
  2466. /******************************************************************************/
  2467. Bool ValidEmail(C Str &email) // "[email protected]"
  2468. {
  2469. if( email.length() >=5 // [email protected]
  2470. && U16(email.first ())>32 // not space, tab, new line, ..
  2471. && U16(email.last ())>32) // not space, tab, new line, ..
  2472. {
  2473. Int at =TextPosI(email, '@');
  2474. if( at>=1 && TextPosI(email()+at+1, '@')<0) // if found '@' at 1 or higher position and there's no other '@' after
  2475. {
  2476. Int dot =TextPosI(email()+at+1, '.');
  2477. if( dot>=1) // if found '.' at 1 or higher position
  2478. if(email.length()>at+2+dot) // if there's something after the dot
  2479. return true;
  2480. }
  2481. }
  2482. return false;
  2483. }
  2484. Bool ValidURL(C Str &url) // "http://domain.com"
  2485. {
  2486. if(StartsPath(url, "http://")
  2487. || StartsPath(url, "https://"))
  2488. {
  2489. Int domain =TextPosI(url, ':');
  2490. if( domain>=0)
  2491. {
  2492. domain+=3; // skip "://", in above case to position "domain.com"
  2493. Int dot =TextPosI(url()+domain, '.');
  2494. if( dot>=1) // if found '.' at 1 or higher position
  2495. if(url.length()>domain+dot+1) // if there's something after the dot
  2496. return true;
  2497. }
  2498. }
  2499. return false;
  2500. }
  2501. Bool ValidLicenseKey(C Str &key) // "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX" 5*5X + 4*-
  2502. {
  2503. if(key.length()!=5*5+4)return false;
  2504. if(key[5]!='-' || key[11]!='-' || key[17]!='-' || key[23]!='-')return false;
  2505. REP(5)
  2506. {
  2507. Int offset=i*6;
  2508. REP(5)
  2509. {
  2510. Char c=key[offset+i];
  2511. if(!((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9')))return false;
  2512. }
  2513. }
  2514. return true;
  2515. }
  2516. /******************************************************************************/
  2517. UID DeviceID(Bool per_user)
  2518. {
  2519. if(!D.deviceName().is()) // display device name may be not available yet
  2520. {
  2521. SafeSyncLocker locker(D._lock);
  2522. #if WINDOWS_OLD
  2523. if(IDirect3D9 *d3d=Direct3DCreate9(D3D_SDK_VERSION))
  2524. {
  2525. D3DADAPTER_IDENTIFIER9 id; if(OK(d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &id)))D._device_name=id.Description;
  2526. RELEASE(d3d);
  2527. }
  2528. #elif WINDOWS_NEW
  2529. IDXGIFactory1 *factory=null; CreateDXGIFactory1(__uuidof(IDXGIFactory1), (Ptr*)&factory); if(factory)
  2530. {
  2531. IDXGIAdapter *adapter=null; factory->EnumAdapters(0, &adapter); if(adapter)
  2532. {
  2533. DXGI_ADAPTER_DESC desc; if(OK(adapter->GetDesc(&desc)))D._device_name=desc.Description;
  2534. adapter->Release();
  2535. }
  2536. factory->Release();
  2537. }
  2538. #elif APPLE // Mac/iOS already have a context
  2539. D._device_name=(CChar8*)glGetString(GL_RENDERER);
  2540. #elif LINUX
  2541. int attribs[]=
  2542. {
  2543. GLX_X_RENDERABLE , true,
  2544. GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
  2545. GLX_RENDER_TYPE , GLX_RGBA_BIT,
  2546. GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
  2547. //GLX_RED_SIZE , 8,
  2548. //GLX_GREEN_SIZE , 8,
  2549. //GLX_BLUE_SIZE , 8,
  2550. //GLX_ALPHA_SIZE , 8,
  2551. //GLX_DEPTH_SIZE , 24,
  2552. //GLX_STENCIL_SIZE , 8,
  2553. //GLX_DOUBLEBUFFER , true,
  2554. NULL
  2555. };
  2556. int count=0; if(GLXFBConfig *fbc=glXChooseFBConfig(XDisplay, DefaultScreen(XDisplay), attribs, &count))
  2557. {
  2558. if(count>=1 && fbc[0])
  2559. if(GLXContext context=glXCreateNewContext(XDisplay, fbc[0], GLX_RGBA_TYPE, null, true))
  2560. {
  2561. XSync(XDisplay, false); // Forcibly wait on any resulting X errors
  2562. if(glXMakeCurrent(XDisplay, NULL, context))
  2563. {
  2564. D._device_name=(CChar8*)glGetString(GL_RENDERER); for(; D._device_name.last()==' '; )D._device_name.removeLast(); // Linux may have unnecessary spaces at the end
  2565. glXMakeCurrent(XDisplay, NULL, null);
  2566. }
  2567. glXDestroyContext(XDisplay, context);
  2568. }
  2569. XFree(fbc);
  2570. }
  2571. #elif ANDROID
  2572. if(EGLDisplay display=eglGetDisplay(EGL_DEFAULT_DISPLAY))
  2573. {
  2574. if(eglInitialize(display, null, null)==EGL_TRUE)
  2575. {
  2576. EGLint attribs[]=
  2577. {
  2578. EGL_SURFACE_TYPE , EGL_WINDOW_BIT,
  2579. EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  2580. EGL_NONE
  2581. };
  2582. EGLConfig config=0;
  2583. EGLint num_configs=0;
  2584. if(eglChooseConfig(display, attribs, &config, 1, &num_configs)==EGL_TRUE)
  2585. if(num_configs>=1)
  2586. if(EGLSurface surface=eglCreateWindowSurface(display, config, AndroidApp->window, null))
  2587. {
  2588. EGLint ctx_attribs[]=
  2589. {
  2590. EGL_CONTEXT_CLIENT_VERSION, 2, // try OpenGL ES 2.0 context
  2591. EGL_NONE
  2592. };
  2593. if(EGLContext context=eglCreateContext(display, config, null, ctx_attribs))
  2594. {
  2595. if(eglMakeCurrent(display, surface, surface, context)==EGL_TRUE)
  2596. {
  2597. D._device_name=(CChar8*)glGetString(GL_RENDERER);
  2598. eglMakeCurrent(display, null, null, null);
  2599. }
  2600. eglDestroyContext(display, context);
  2601. }
  2602. eglDestroySurface(display, surface);
  2603. }
  2604. }
  2605. eglTerminate(display);
  2606. }
  2607. #endif
  2608. if(!D.deviceName().is())D._device_name="None"; // to avoid calling above everytime
  2609. }
  2610. Str id=S
  2611. +Cpu.name()+'\n'
  2612. +D.deviceName()+'\n'
  2613. +OSName(OSGroup())+'\n'
  2614. +DeviceManufacturer()+'\n'
  2615. +DeviceModel()+'\n'
  2616. +DeviceSerialNumber()+'\n'
  2617. +(per_user ? OSUserName() : S)+'\n';
  2618. MD5 hash;
  2619. hash.update(id);
  2620. #if ANDROID && 0 // don't do this because of "app-signing key" dependency
  2621. if(per_user){ULong android_id=AndroidID(); hash.update(&android_id, SIZE(android_id));} // 'AndroidID' depends on app-signing key, user and device
  2622. #endif
  2623. ULong mac=GetMac(); hash.update(&mac, SIZE(mac));
  2624. return hash();
  2625. }
  2626. Str DeviceManufacturer()
  2627. {
  2628. #if ANDROID
  2629. JNI jni;
  2630. if(jni && ActivityClass)
  2631. if(JMethodID method=jni->GetStaticMethodID(ActivityClass, "manufacturer", "()Ljava/lang/String;"))
  2632. if(JString str=JString(jni, jni->CallStaticObjectMethod(ActivityClass, method)))
  2633. return str.str();
  2634. #elif APPLE
  2635. return "Apple";
  2636. #endif
  2637. return S;
  2638. }
  2639. Str DeviceModel()
  2640. {
  2641. #if ANDROID
  2642. JNI jni;
  2643. if(jni && ActivityClass)
  2644. if(JMethodID method=jni->GetStaticMethodID(ActivityClass, "model", "()Ljava/lang/String;"))
  2645. if(JString str=JString(jni, jni->CallStaticObjectMethod(ActivityClass, method)))
  2646. return str.str();
  2647. #endif
  2648. return S;
  2649. }
  2650. Str8 DeviceSerialNumber()
  2651. {
  2652. #if ANDROID
  2653. JNI jni;
  2654. if(jni && ActivityClass)
  2655. if(JMethodID method=jni->GetStaticMethodID(ActivityClass, "serial", "()Ljava/lang/String;"))
  2656. if(JString str=JString(jni, jni->CallStaticObjectMethod(ActivityClass, method)))
  2657. return str.str();
  2658. #endif
  2659. return S;
  2660. }
  2661. ULong AndroidID()
  2662. {
  2663. #if ANDROID
  2664. JNI jni;
  2665. if(jni && ActivityClass)
  2666. if(JMethodID method=jni->GetStaticMethodID(ActivityClass, "androidID", "()Ljava/lang/String;"))
  2667. if(JString str=JString(jni, jni->CallStaticObjectMethod(ActivityClass, method)))
  2668. {
  2669. Char8 id[2+16+1]; // 0x + 0000000000000000 + nul
  2670. Set(id, "0x"); Append(id, str.str());
  2671. return TextULong(id);
  2672. }
  2673. #endif
  2674. return 0;
  2675. }
  2676. /******************************************************************************/
  2677. Str MicrosoftWindowsStoreLink(C Str &app_id) {return S+"https://www.microsoft.com/store/apps/" +app_id;}
  2678. Str AppleAppStoreLink(C Str &app_id) {return S+"https://itunes.apple.com/app/id" +app_id;}
  2679. Str GooglePlayStoreLink(C Str &app_id) {return S+"https://play.google.com/store/apps/details?id="+app_id;}
  2680. /******************************************************************************/
  2681. Str CString(C Str &str)
  2682. {
  2683. Str temp; temp.reserve(str.length());
  2684. FREPA(str)
  2685. {
  2686. Char c=str()[i]; // () avoids range check
  2687. switch(c)
  2688. {
  2689. case '\\': // !! no break on purpose !!
  2690. case '"' : temp+='\\'; // !! no break on purpose !!
  2691. default : temp+=c ; break;
  2692. case '\n': temp+="\\n"; break;
  2693. case '\r': temp+="\\r"; break;
  2694. case '\t': temp+="\\t"; break;
  2695. case '\0': temp+="\\0"; break;
  2696. }
  2697. }
  2698. return temp;
  2699. }
  2700. Str XmlString(C Str &str)
  2701. {
  2702. Str temp; temp.reserve(str.length());
  2703. FREPA(str)
  2704. {
  2705. Char c=str()[i]; // () avoids range check
  2706. if(c=='&' )temp+="&amp;" ;else
  2707. if(c=='<' )temp+="&lt;" ;else
  2708. if(c=='>' )temp+="&gt;" ;else
  2709. if(c=='\'')temp+="&apos;";else
  2710. if(c=='"' )temp+="&quot;";else
  2711. temp+=c;
  2712. }
  2713. return temp;
  2714. }
  2715. Str DecodeXmlString(C Str &str)
  2716. {
  2717. Str temp; temp.reserve(str.length());
  2718. FREPA(str)
  2719. {
  2720. Char c=str()[i]; // () avoids range check
  2721. if( c=='&') // special
  2722. {
  2723. if(str[i+1]=='l' && str[i+2]=='t' && str[i+3]==';'){temp+='<' ; i+=3; continue;}
  2724. if(str[i+1]=='g' && str[i+2]=='t' && str[i+3]==';'){temp+='>' ; i+=3; continue;}
  2725. if(str[i+1]=='a' && str[i+2]=='p' && str[i+3]=='o' && str[i+4]=='s' && str[i+5]==';'){temp+='\''; i+=5; continue;}
  2726. if(str[i+1]=='q' && str[i+2]=='u' && str[i+3]=='o' && str[i+4]=='t' && str[i+5]==';'){temp+='"' ; i+=5; continue;}
  2727. if(str[i+1]=='a' && str[i+2]=='m' && str[i+3]=='p' && str[i+4]==';'){temp+='&' ; i+=4; continue;}
  2728. }
  2729. temp+=c;
  2730. }
  2731. return temp;
  2732. }
  2733. /******************************************************************************/
  2734. static CChar* GetPath2(CChar *name, Char (&dest)[MAX_LONG_PATH]=ConstCast(TempChar<MAX_LONG_PATH>()).c)
  2735. {
  2736. if(Int length=SetReturnLength(dest, name))for(; length--; )if(IsSlash(dest[length])){dest[length]=0; return dest;}
  2737. dest[0]=0; return dest;
  2738. }
  2739. static CChar* GetBase2(CChar *name)
  2740. {
  2741. if(Int length=Length(name))
  2742. {
  2743. for(; length--; )if(IsSlash(name[length]))break;
  2744. return name+length+1;
  2745. }
  2746. return null;
  2747. }
  2748. REG_KEY_TYPE GetReg(REG_KEY_GROUP reg_key_group, C Str &name, Memc<Byte> *data)
  2749. {
  2750. if(data)data->clear();
  2751. REG_KEY_TYPE type=REG_KEY_NONE;
  2752. #if WINDOWS_OLD
  2753. HKEY group, key;
  2754. switch(reg_key_group)
  2755. {
  2756. case RKG_CLASSES_ROOT : group=HKEY_CLASSES_ROOT ; break;
  2757. case RKG_CURRENT_USER : group=HKEY_CURRENT_USER ; break;
  2758. case RKG_LOCAL_MACHINE: group=HKEY_LOCAL_MACHINE; break;
  2759. case RKG_USERS : group=HKEY_USERS ; break;
  2760. default : return REG_KEY_NONE;
  2761. }
  2762. if(RegOpenKeyExW(group, Replace(GetPath2(name), '/', '\\'), 0, KEY_QUERY_VALUE , &key)!=ERROR_SUCCESS)
  2763. #if X64 // try 32-bit
  2764. if(RegOpenKeyExW(group, Replace(GetPath2(name), '/', '\\'), 0, KEY_QUERY_VALUE|KEY_WOW64_32KEY, &key)!=ERROR_SUCCESS)return type;
  2765. #else // try 64-bit
  2766. if(RegOpenKeyExW(group, Replace(GetPath2(name), '/', '\\'), 0, KEY_QUERY_VALUE|KEY_WOW64_64KEY, &key)!=ERROR_SUCCESS)return type;
  2767. #endif
  2768. CChar *base=GetBase2(name);
  2769. DWORD dwType, dwSize;
  2770. if(RegQueryValueExW(key, WChar(base), null, &dwType, null, &dwSize)==ERROR_SUCCESS)
  2771. {
  2772. switch(dwType)
  2773. {
  2774. case REG_DWORD : type=REG_KEY_U32 ; break;
  2775. case REG_QWORD : type=REG_KEY_U64 ; break;
  2776. case REG_SZ : type=REG_KEY_STRING; break;
  2777. case REG_BINARY: type=REG_KEY_DATA ; break;
  2778. }
  2779. if(type && data)
  2780. {
  2781. data->setNum(dwSize);
  2782. if(RegQueryValueExW(key, WChar(base), null, null, data->data(), &dwSize)!=ERROR_SUCCESS)data->clear();
  2783. }
  2784. }
  2785. RegCloseKey(key);
  2786. #endif
  2787. return type;
  2788. }
  2789. Str GetRegStr(REG_KEY_GROUP reg_key_group, C Str &name, Bool *success)
  2790. {
  2791. Memc<Byte> data;
  2792. switch(GetReg(reg_key_group, name, &data))
  2793. {
  2794. case REG_KEY_STRING:
  2795. {
  2796. if(success)*success=true;
  2797. return (Char*)data.data();
  2798. }break;
  2799. }
  2800. if(success)*success=false;
  2801. return S;
  2802. }
  2803. UInt GetRegUInt(REG_KEY_GROUP reg_key_group, C Str &name, Bool *success)
  2804. {
  2805. Memc<Byte> data;
  2806. switch(GetReg(reg_key_group, name, &data))
  2807. {
  2808. case REG_KEY_U32:
  2809. case REG_KEY_U64:
  2810. {
  2811. if(success)*success=true;
  2812. return *(UInt*)data.data();
  2813. }break;
  2814. }
  2815. if(success)*success=false;
  2816. return 0;
  2817. }
  2818. static Bool SetReg(REG_KEY_GROUP reg_key_group, C Str &name, REG_KEY_TYPE reg_key_type, CPtr data, Int size)
  2819. {
  2820. Bool ok=false;
  2821. #if WINDOWS_OLD
  2822. UInt type;
  2823. HKEY key;
  2824. switch(reg_key_type)
  2825. {
  2826. case REG_KEY_DATA : type=REG_BINARY; break;
  2827. case REG_KEY_STRING: type=REG_SZ ; break;
  2828. case REG_KEY_U32 : type=REG_DWORD ; break;
  2829. case REG_KEY_U64 : type=REG_QWORD ; break;
  2830. default : return false;
  2831. }
  2832. switch(reg_key_group)
  2833. {
  2834. case RKG_CLASSES_ROOT : key=HKEY_CLASSES_ROOT ; break;
  2835. case RKG_CURRENT_USER : key=HKEY_CURRENT_USER ; break;
  2836. case RKG_LOCAL_MACHINE: key=HKEY_LOCAL_MACHINE; break;
  2837. case RKG_USERS : key=HKEY_USERS ; break;
  2838. default : return false;
  2839. }
  2840. if(RegCreateKeyEx(key, Replace(GetPath2(name), '/', '\\'), 0, null, 0, KEY_WRITE, null, &key, null)==ERROR_SUCCESS)
  2841. {
  2842. if(RegSetValueEx(key, WChar(GetBase2(name)), 0, type, (const BYTE*)data, size)==ERROR_SUCCESS)
  2843. {
  2844. ok=true;
  2845. }
  2846. RegCloseKey(key);
  2847. }
  2848. #endif
  2849. return ok;
  2850. }
  2851. Bool SetRegStr(REG_KEY_GROUP reg_key_group, C Str &name, C Str &value)
  2852. {
  2853. return value.is() ? SetReg(reg_key_group, name, REG_KEY_STRING, value(), (value.length()+1)*SIZE(Char)) // include null-terminated zero, and multiply by size of Char
  2854. : SetReg(reg_key_group, name, REG_KEY_STRING, null , 0); // this is OK for empty string
  2855. }
  2856. Bool SetRegUInt(REG_KEY_GROUP reg_key_group, C Str &name, UInt value)
  2857. {
  2858. return SetReg(reg_key_group, name, REG_KEY_U32, &value, SIZE(value));
  2859. }
  2860. Bool SetRegData(REG_KEY_GROUP reg_key_group, C Str &name, CPtr data, Int size)
  2861. {
  2862. return SetReg(reg_key_group, name, REG_KEY_DATA, data, size);
  2863. }
  2864. /******************************************************************************/
  2865. UID FileNameID(C Str &name)
  2866. {
  2867. UID id; if(name.is() && DecodeFileName(_GetBase(name), id))return id;
  2868. return UIDZero;
  2869. }
  2870. /******************************************************************************/
  2871. VecI4 FileVersion(C Str &name)
  2872. {
  2873. #if WINDOWS_OLD
  2874. if(UInt size=GetFileVersionInfoSize(name, null))
  2875. {
  2876. Memt<Byte> data; data.setNum(size);
  2877. if(GetFileVersionInfo(name, 0, data.elms(), data.data()))
  2878. {
  2879. VS_FIXEDFILEINFO *info=null;
  2880. UINT info_size=0;
  2881. if(VerQueryValue(data.data(), L"\\", (Ptr*)&info, &info_size))if(info && info_size==SIZE(VS_FIXEDFILEINFO))
  2882. {
  2883. UInt MajorVersion =HIWORD(info->dwFileVersionMS),
  2884. MinorVersion =LOWORD(info->dwFileVersionMS),
  2885. BuildNumber =HIWORD(info->dwFileVersionLS),
  2886. RevisionNumber=LOWORD(info->dwFileVersionLS);
  2887. return VecI4(MajorVersion, MinorVersion, BuildNumber, RevisionNumber);
  2888. }
  2889. }
  2890. }
  2891. #endif
  2892. return -1;
  2893. }
  2894. /******************************************************************************/
  2895. Int TextPatch::diffLength()C
  2896. {
  2897. Int d=0;
  2898. REPA(diffs)
  2899. {
  2900. C Diff &diff=diffs[i]; if(diff.mode!=EQUAL)d+=diff.text.length();
  2901. }
  2902. return d;
  2903. }
  2904. Int Difference(C Str &a, C Str &b)
  2905. {
  2906. Memt<TextPatch> patches; Merge(a, b, b, patches);
  2907. Int d=0; REPA(patches)d+=patches[i].diffLength(); return d;
  2908. }
  2909. #if 1 // use EE-base Qt
  2910. struct QStringList;
  2911. typedef UShort ushort;
  2912. typedef UInt uint;
  2913. T2(A, B) struct QPair : std__pair<A, B>
  2914. {
  2915. QPair() {}
  2916. QPair(C A &a, C B &b) : std__pair<A, B>(a, b) {}
  2917. };
  2918. T1(TYPE) struct QVector : std__vector<TYPE>
  2919. {
  2920. QVector() {}
  2921. QVector(Int elms) : std__vector<TYPE>(elms) {}
  2922. };
  2923. T1(TYPE) struct QList
  2924. {
  2925. Memx<TYPE> _; // use 'Memx' because some codes assume that elements are in const memory address
  2926. int size ()C {return _.elms();}
  2927. int count()C {return _.elms();}
  2928. bool empty()C {return _.elms()<=0;}
  2929. bool isEmpty()C {return _.elms()<=0;}
  2930. TYPE& operator[](int i) {return _[i];}
  2931. C TYPE& operator[](int i)C {return _[i];}
  2932. TYPE& first() {return _.first();}
  2933. C TYPE& first()C {return _.first();}
  2934. TYPE& last() {return _.last();}
  2935. C TYPE& last()C {return _.last();}
  2936. TYPE& front() {return _.first();}
  2937. C TYPE& front()C {return _.first();}
  2938. TYPE& back() {return _.last();}
  2939. C TYPE& back()C {return _.last();}
  2940. TYPE value(int i)C {return InRange(i, _) ? _[i] : TYPE();}
  2941. void clear() {_.clear();}
  2942. void append(C TYPE &value) {_.New ( )=value;}
  2943. void prepend(C TYPE &value) {_.NewAt(0)=value;}
  2944. void removeFirst() {_.removeValid(0, true);}
  2945. void removeLast () {_.removeLast ( );}
  2946. void operator+=(C TYPE &value) {_.New()=value;}
  2947. void operator+=(C QList<TYPE> &other) {FREPA(other)T+=other[i];}
  2948. QList<TYPE> operator+ (C QList<TYPE> &other)C {QList<TYPE> temp=T; temp+=other; return temp;}
  2949. };
  2950. T1(TYPE) static inline Int Elms(C QList<TYPE> &q) {return q.size();}
  2951. T1(TYPE) struct QStack : QList<TYPE>
  2952. {
  2953. void push(C TYPE &data) {QList<TYPE>::_.New()=data;}
  2954. TYPE& top ( ) {return QList<TYPE>::_.last();}
  2955. TYPE pop ( ) {TYPE last=QList<TYPE>::_.last(); QList<TYPE>::_.removeLast(); return last;}
  2956. };
  2957. T1(TYPE) struct QMutableListIterator
  2958. {
  2959. QList<TYPE> &list;
  2960. int index;
  2961. bool dir;
  2962. bool hasNext ()C {return InRange(index , list);}
  2963. bool hasPrevious()C {return InRange(index-1, list);}
  2964. void toFront() {index=0;}
  2965. TYPE& next () {dir=true ; return list[index++];}
  2966. TYPE& previous() {dir=false; return list[--index];}
  2967. void remove() {list._.removeValid(dir ? --index : index, true);}
  2968. void setValue(C TYPE &value) {list._[dir ? index-1 : index]=value;}
  2969. void insert (C TYPE &value) {list._.NewAt(index++)=value; dir=true;}
  2970. QMutableListIterator(QList<TYPE> &list) : list(list), index(0), dir(true) {}
  2971. };
  2972. struct QChar
  2973. {
  2974. Char _;
  2975. ushort unicode()C {return _;}
  2976. ushort& unicode() {return (ushort&)_;} ASSERT(SIZE(Char)==SIZE(ushort));
  2977. Char8 toAscii()C {return Char16To8Fast(_);} // we can assume that Str was already initialized
  2978. bool operator==(C QChar &c)C {return _==c._;}
  2979. bool operator!=(C QChar &c)C {return _!=c._;}
  2980. bool isControl ()C {return _=='\r' || _=='\n';}
  2981. bool isSpace ()C {return _==' ' || _=='\t' || _=='\n' || _=='\v' || _=='\f' || _=='\r';}
  2982. bool isLetterOrNumber()C {return CharType(_)==CHART_CHAR;}
  2983. QChar( ) {_='\0';}
  2984. QChar(Char8 c) {_=c;}
  2985. QChar(Char c) {_=c;}
  2986. QChar(ushort c) {_=c;}
  2987. };
  2988. struct QString
  2989. {
  2990. enum SplitBehavior
  2991. {
  2992. KeepEmptyParts,
  2993. SkipEmptyParts,
  2994. };
  2995. Str _;
  2996. QString( ) {}
  2997. QString(QChar c) : _(c._) {}
  2998. QString(CChar8 c) : _(c ) {}
  2999. QString(CChar c) : _(c ) {}
  3000. QString(CChar8 *t) : _(t ) {}
  3001. QString(CChar *t) : _(t ) {}
  3002. QString(C Str8 &s) : _(s ) {}
  3003. QString(C Str &s) : _(s ) {}
  3004. QString(C QString &s) : _(s._) {}
  3005. int length()C {return _.length();}
  3006. C QChar* unicode()C {return (QChar*)_();} ASSERT(SIZE(Char)==SIZE(QChar));
  3007. QString operator+ (C QChar &c)C {return _+c._;}
  3008. QString operator+ (C QString &s)C {return _+s._;}
  3009. QString& operator+=( Char8 c) {_+=c ; return T;}
  3010. QString& operator+=( Char c) {_+=c ; return T;}
  3011. QString& operator+=(C QChar &c) {_+=c._; return T;}
  3012. QString& operator+=(C QString &s) {_+=s._; return T;}
  3013. QString& append(C QChar &c) {T+=c; return T;}
  3014. QString& append(C QString &s) {T+=s; return T;}
  3015. bool operator==(C QString &s)C {return Equal(_, s._, true);}
  3016. bool operator!=(C QString &s)C {return !Equal(_, s._, true);}
  3017. bool isNull ()C {return length()<=0;} // _()==null;} check the same as 'isEmpty' because of different string implementation in EE vs Qt
  3018. bool isEmpty()C {return length()<=0;}
  3019. bool endsWith(C QString &s)C {return Ends (_, s._, true);}
  3020. bool startsWith(C QString &s)C {return Starts(_, s._, true);}
  3021. bool isBlankLineEnd()C // regexp "\\n\\r?\\n$"
  3022. {
  3023. int i=_.length()-1; // last char pos
  3024. if(_[i]=='\n')
  3025. {
  3026. i--;
  3027. if(_[i]=='\r')i--;
  3028. if(_[i]=='\n')return true;
  3029. }
  3030. return false;
  3031. }
  3032. bool isBlankLineStart()C // regexp "^\\r?\\n\\r?\\n"
  3033. {
  3034. int i=0; // first char pos
  3035. if(_[i]=='\r')i++;
  3036. if(_[i]=='\n')
  3037. {
  3038. i++;
  3039. if(_[i]=='\r')i++;
  3040. if(_[i]=='\n')return true;
  3041. }
  3042. return false;
  3043. }
  3044. int toInt()C {return TextInt(_);}
  3045. QString left(int n)C
  3046. {
  3047. if(n<0 || n>length())n=length();
  3048. Str s; s.reserve(n); FREP(n)s+=_[i]; return s;
  3049. }
  3050. QString right(int n)C
  3051. {
  3052. if(n<0 || n>length())n=length();
  3053. Str s; s.reserve(n); int o=length()-n; FREP(n)s+=_[o+i]; return s;
  3054. }
  3055. QString mid(int position, int n=-1)C
  3056. {
  3057. Clamp(position, 0, length());
  3058. int left=length()-position; if(n<0 || n>left)n=left;
  3059. Str s; s.reserve(n); FREP(n)s+=_[position+i]; return s;
  3060. }
  3061. QChar& operator[](int i) {return (QChar&)(_()[i]);}
  3062. C QChar operator[](int i)C {return _ [i] ;}
  3063. int indexOf(C QString &s, int from=0)C
  3064. {
  3065. // following code matches Qt (do not change)
  3066. if(from<0)from+=length();
  3067. if(uint(s.length()+from)>(uint)length())return -1;
  3068. if(!s.length())return from;
  3069. if(! length())return -1;
  3070. Clamp(from, 0, length());
  3071. int index=TextPosI(_()+from, s._, true);
  3072. return (index>=0) ? index+from : -1;
  3073. }
  3074. int lastIndexOf(C QString &s, int from=-1)C
  3075. {
  3076. // following code matches Qt (do not change)
  3077. if(from<0)from+=length();
  3078. if(from==length() && s.length()==0)return from;
  3079. int delta=length()-s.length();
  3080. if(from<0 || from>=length() || delta<0)return -1;
  3081. if(from>delta)from=delta;
  3082. if(!s.length())return from; // this line was added for Qt compatibility (do not change)
  3083. for(; from>=0; from--)if(Starts(_()+from, s._(), true))return from;
  3084. return -1;
  3085. }
  3086. void replace( Char from, Char to) {_=Replace(_, from, to);}
  3087. QString& replace(C Str &from, C Str &to) {_=Replace(_, from, to); return T;}
  3088. QStringList split(Char c, SplitBehavior behavior=KeepEmptyParts)C;
  3089. static QString number(int n) {return TextInt(n);}
  3090. static QString number(float n) {return TextFlt(n);}
  3091. };
  3092. static inline Int Elms(C QString &q) {return q.length();}
  3093. /*struct QUrl
  3094. {
  3095. static QString toPercentEncoding(C QString &input, C QString &exclude=QString());
  3096. {
  3097. Bool normal[256]; Zero(normal);
  3098. for(int i=0x61; i<=0x7A; i++)normal[i]=true; // ALPHA
  3099. for(int i=0x41; i<=0x5A; i++)normal[i]=true; // ALPHA
  3100. for(int i=0x30; i<=0x39; i++)normal[i]=true; // DIGIT
  3101. normal[0x2D]=true; // -
  3102. normal[0x2E]=true; // .
  3103. normal[0x5F]=true; // _
  3104. normal[0x7E]=true; // ~
  3105. REPA(exclude)if(InRange(exclude[i]._, normal))normal[exclude[i]._]=true;
  3106. QString out;
  3107. FREPA(input)
  3108. {
  3109. QChar c=input[i];
  3110. Byte bc=Byte(c._);
  3111. if(InRange(c._, normal) ? normal[bc] : true)out+=c;else
  3112. {
  3113. out+='%';
  3114. out+=Digits16[bc>> 4];
  3115. out+=Digits16[bc&0xF];
  3116. }
  3117. }
  3118. return out;
  3119. }
  3120. static QString fromPercentEncoding(C QString &input)
  3121. {
  3122. QString out;
  3123. FREPA(input)
  3124. {
  3125. Char c=input._[i];
  3126. if(c!='%')out+=c;else
  3127. {
  3128. UShort h=input._[++i];
  3129. UShort l=input._[++i];
  3130. if(h>='0' && h<='9')h-='0' ;else
  3131. if(h>='a' && h<='f')h-='a'-10;else
  3132. if(h>='A' && h<='F')h-='A'-10;else continue;
  3133. if(l>='0' && l<='9')l-='0' ;else
  3134. if(l>='a' && l<='f')l-='a'-10;else
  3135. if(l>='A' && l<='F')l-='A'-10;else continue;
  3136. out+=Char((h<<4)|l);
  3137. }
  3138. }
  3139. return out;
  3140. }
  3141. };
  3142. struct QRegExp
  3143. {
  3144. };*/
  3145. struct QStringList : QList<QString>
  3146. {
  3147. QStringList& operator<<(C QString &s) {append(s); return T;}
  3148. };
  3149. QStringList QString::split(Char c, SplitBehavior behavior)C
  3150. {
  3151. Memc<Str> s=Split(_, c);
  3152. QStringList list;
  3153. FREPA(s)
  3154. if(behavior==KeepEmptyParts || s[i].is())
  3155. list<<s[i];
  3156. return list;
  3157. }
  3158. struct QVariant
  3159. {
  3160. QString string;
  3161. QStringList stringlist;
  3162. static QVariant fromValue(C QString &s ) {QVariant v; v.string =s ; return v;}
  3163. static QVariant fromValue(C QStringList &sl) {QVariant v; v.stringlist=sl; return v;}
  3164. C QString & toString ()C {return string ;}
  3165. C QStringList& toStringList()C {return stringlist;}
  3166. };
  3167. T2(KEY, DATA) struct QMap
  3168. {
  3169. bool contains(C KEY &key)C {return ConstCast(_).find(key)!=null;}
  3170. DATA value(C KEY &key, C DATA &defaultValue=DATA(0)) {if(DATA *data=_.find(key))return *data; return defaultValue;}
  3171. void insert(C KEY &key, C DATA &value) {if(DATA *data=_.get(key))*data=value;}
  3172. QMap& operator=(C QMap &map) {_=map._; return T;}
  3173. QMap();
  3174. QMap(C QMap &map);
  3175. private:
  3176. Map<KEY, DATA> _;
  3177. };
  3178. static Int CompareQString(C QString &a, C QString &b) {return CompareCS(a._, b._);}
  3179. static Int CompareQChar (C QChar &a, C QChar &b) {return Compare ((UShort)a._, (UShort)b._);}
  3180. template<> QMap<QString, int>::QMap( ) : _(CompareQString, null) {}
  3181. template<> QMap<QString, int>::QMap(C QMap &map) : _(CompareQString, null) {T=map;}
  3182. template<> QMap<QChar , int>::QMap( ) : _(CompareQChar , null) {}
  3183. template<> QMap<QChar , int>::QMap(C QMap &map) : _(CompareQChar , null) {T=map;}
  3184. //} // close EE namespace to include "std" headers in global namespace (not needed since no headers are actually loaded in the 'diff_match_patch' at the moment)
  3185. #undef DELETE
  3186. #include "../ThirdPartyLibs/Diff Match Patch/diff_match_patch.cpp"
  3187. //namespace EE{
  3188. Str Merge(C Str &base, C Str &a, C Str &b, MemPtr<TextPatch> patches, Int timeout)
  3189. {
  3190. if(!patches)
  3191. {
  3192. if(Equal(base, a, true))return b; // if 'a' didn't apply any changes then return 'b'
  3193. if(Equal(base, b, true))return a; // if 'b' didn't apply any changes then return 'a'
  3194. if(Equal( a, b, true))return a; // if 'a' and 'b' had the same changes applied then they're the same
  3195. }
  3196. diff_match_patch dmp; dmp.Diff_Timeout=((timeout<=0) ? 0.0f : timeout/1000.0f);
  3197. QList<Patch> dmp_patches=dmp.patch_make(base, a);
  3198. C QPair<QString, QVector<bool> > &out=dmp.patch_apply(dmp_patches, b);
  3199. if(patches)
  3200. {
  3201. patches.setNum(Min(dmp_patches.size(), out.second.size()));
  3202. FREPA(patches)
  3203. {
  3204. C Patch &dmp_patch=dmp_patches[i];
  3205. TextPatch & patch= patches[i];
  3206. patch.ok =out.second[i];
  3207. patch.base_offset=dmp_patch.start1; patch.base_length=dmp_patch.length1;
  3208. patch. a_offset=dmp_patch.start2; patch. a_length=dmp_patch.length2;
  3209. patch.diffs.setNum(dmp_patch.diffs.size());
  3210. FREPA(patch.diffs)
  3211. {
  3212. C Diff &dmp_diff=dmp_patch.diffs[i];
  3213. TextPatch::Diff & diff= patch.diffs[i];
  3214. diff.text=dmp_diff.text._;
  3215. switch(dmp_diff.operation)
  3216. {
  3217. case DELETE: diff.mode=TextPatch::DEL ; break;
  3218. case INSERT: diff.mode=TextPatch::ADD ; break;
  3219. default : diff.mode=TextPatch::EQUAL; break;
  3220. }
  3221. }
  3222. }
  3223. }
  3224. return out.first._;
  3225. }
  3226. #elif 0 // use native Qt
  3227. } // close EE namespace to include "std" headers in global namespace (not needed since no headers are actually loaded in the 'diff_match_patch' at the moment)
  3228. #undef DELETE
  3229. #undef T
  3230. #undef C
  3231. #undef T1
  3232. #undef T2
  3233. #undef T3
  3234. #include "Libs/Diff Match Patch/diff_match_patch (original).cpp"
  3235. #define T (*this)
  3236. #define C const
  3237. namespace EE{
  3238. QString QStr(C Str &s) {QString q; FREPA(s)q+=QChar(s[i]); return q;}
  3239. Str QStr(C QString &q) {return (CChar*)q.unicode();}
  3240. Str Merge(C Str &base, C Str &a, C Str &b, MemPtr<TextPatch> patches, Int timeout)
  3241. {
  3242. if(!patches)
  3243. {
  3244. if(Equal(base, a, true))return b; // if 'a' didn't apply any changes then return 'b'
  3245. if(Equal(base, b, true))return a; // if 'b' didn't apply any changes then return 'a'
  3246. if(Equal( a, b, true))return a; // if 'a' and 'b' had the same changes applied then they're the same
  3247. }
  3248. diff_match_patch dmp; dmp.Diff_Timeout=((timeout<=0) ? 0.0f : timeout/1000.0f);
  3249. QList<Patch> dmp_patches=dmp.patch_make(QStr(base), QStr(a));
  3250. C QPair<QString, QVector<bool> > &out=dmp.patch_apply(dmp_patches, QStr(b));
  3251. if(patches)
  3252. {
  3253. patches.setNum(Min(dmp_patches.size(), out.second.size()));
  3254. FREPA(patches)
  3255. {
  3256. C Patch &dmp_patch=dmp_patches[i];
  3257. TextPatch & patch= patches[i];
  3258. patch.ok =out.second[i];
  3259. patch.base_offset=dmp_patch.start1; patch.base_length=dmp_patch.length1;
  3260. patch. a_offset=dmp_patch.start2; patch. a_length=dmp_patch.length2;
  3261. patch.diffs.setNum(dmp_patch.diffs.size());
  3262. FREPA(patch.diffs)
  3263. {
  3264. C Diff &dmp_diff=dmp_patch.diffs[i];
  3265. TextPatch::Diff & diff= patch.diffs[i];
  3266. diff.text=QStr(dmp_diff.text);
  3267. switch(dmp_diff.operation)
  3268. {
  3269. case DELETE: diff.mode=TextPatch::DEL ; break;
  3270. case INSERT: diff.mode=TextPatch::ADD ; break;
  3271. default : diff.mode=TextPatch::EQUAL; break;
  3272. }
  3273. }
  3274. }
  3275. }
  3276. return QStr(out.first);
  3277. }
  3278. #else // use STL port
  3279. #if WINDOWS
  3280. #pragma warning(disable:4267) // 64bit - conversion from 'size_t' to 'Int', possible loss of data
  3281. struct std__string
  3282. {
  3283. static INLINE int wmemcmp(CChar8 *a, CChar8 *b, size_type length) {for(; 0<length; ++a, ++b, --length)if(*a!=*b)return (*a<*b) ? -1 : +1; return 0;}
  3284. static INLINE CChar8* wmemchr(CChar8 *t, CChar8 c, size_type length) {for(; 0<length; ++t, --length)if(*t== c)return t; return null;}
  3285. struct traits_type
  3286. {
  3287. static int compare(CChar8 *a, CChar8 *b, size_type length) {return wmemcmp(a, b, length);}
  3288. };
  3289. static const size_type npos=-1;
  3290. typedef Char8 value_type;
  3291. typedef size_t size_type;
  3292. typedef CChar8* const_pointer;
  3293. typedef Char8* iterator;
  3294. Str8 str;
  3295. std__string operator+( Char8 c)C {return str+c ;}
  3296. std__string operator+( Char c)C {return str+c ;}
  3297. std__string operator+(CChar8 *t)C {return str+t ;}
  3298. std__string operator+(CChar *t)C {return str+t ;}
  3299. std__string operator+(C Str8 &s)C {return str+s ;}
  3300. std__string operator+(C Str &s)C {return str+s ;}
  3301. std__string operator+(C std__string &s)C {return str+s.str;}
  3302. std__string& operator+=( Char8 c) {str+=c ; return T;}
  3303. std__string& operator+=( Char c) {str+=c ; return T;}
  3304. std__string& operator+=(CChar8 *t) {str+=t ; return T;}
  3305. std__string& operator+=(CChar *t) {str+=t ; return T;}
  3306. std__string& operator+=(C Str8 &s) {str+=s ; return T;}
  3307. std__string& operator+=(C Str &s) {str+=s ; return T;}
  3308. std__string& operator+=(C std__string &s) {str+=s.str; return T;}
  3309. bool operator==(CChar8 *t)C {return Equal(str, t , true);}
  3310. bool operator==(CChar *t)C {return Equal(str, t , true);}
  3311. bool operator==(C Str8 &s)C {return Equal(str, s , true);}
  3312. bool operator==(C Str &s)C {return Equal(str, s , true);}
  3313. bool operator==(C std__string &s)C {return Equal(str, s.str, true);}
  3314. bool operator!=(CChar8 *t)C {return !Equal(str, t , true);}
  3315. bool operator!=(CChar *t)C {return !Equal(str, t , true);}
  3316. bool operator!=(C Str8 &s)C {return !Equal(str, s , true);}
  3317. bool operator!=(C Str &s)C {return !Equal(str, s , true);}
  3318. bool operator!=(C std__string &s)C {return !Equal(str, s.str, true);}
  3319. iterator begin() {return iterator(c_str());}
  3320. iterator end () {return iterator(c_str()+size());}
  3321. Bool empty()C {return !str.is ();}
  3322. CChar8* c_str()C {return str ();}
  3323. size_type size()C {return str.length();}
  3324. size_type length()C {return str.length();}
  3325. Char8 operator[](Int i )C {return str[i];}
  3326. void clear( ) {str.clear();}
  3327. void resize(int length) {if(length<=size())str.clip(length);else str.reserve(length);}
  3328. bool _Inside(CChar8 *s)C {if(!s || s<c_str() || c_str()+size()<=s)return false;else return true;}
  3329. std__string substr(size_type start, size_type length=npos)C {if(length==npos)length=T.size()-start; Str8 s; s.reserve(length); FREP(length)s+=T[start+i]; return s;}
  3330. int compare(size_type offset, size_type length, C std__string &s)C {return compare(offset, length, s, 0, npos);}
  3331. int compare(size_type offset, size_type length, C std__string &s, size_type offset2, size_type length2)C
  3332. {
  3333. if(s.size()-offset2<length2)length2=s.size()-offset2;
  3334. return compare(offset, length, s.c_str()+offset2, length2);
  3335. }
  3336. int compare(size_type offset, size_type length, CChar8 *s, size_type length2)C
  3337. {
  3338. if(size()-offset<length)length=size()-offset;
  3339. size_type c=wmemcmp(c_str()+offset, s, (length<length2) ? length : length2);
  3340. return (c ? (int)c : (length<length2) ? -1 : (length==length2) ? 0 : +1);
  3341. }
  3342. void swap(std__string &s) {Swap(T, s);}
  3343. size_type find(C std__string &s, size_type offset=0)C {return find(s.c_str(), offset, s.size() );}
  3344. size_type find( CChar8 *s, size_type offset=0)C {return find(s , offset, Length(s));}
  3345. size_type find( CChar8 *s, size_type offset, size_type length)C
  3346. {
  3347. if(length==0 && offset<=size())return offset;
  3348. size_type i;
  3349. if(offset<size() && length<=(i=size()-offset))
  3350. {
  3351. CChar8 *u, *v;
  3352. for(i-=length-1, v=c_str()+offset; u=wmemchr(v, *s, i); i-=u-v+1, v=u+1)
  3353. if(wmemcmp(u, s, length)==0)return u-c_str();
  3354. }
  3355. return npos;
  3356. }
  3357. size_type rfind(C std__string &s, size_type offset=0)C {return rfind(s.c_str(), offset, s.size() );}
  3358. size_type rfind( CChar8 *s, size_type offset=0)C {return rfind(s , offset, Length(s));}
  3359. size_type rfind( CChar8 *s, size_type offset, size_type length)C
  3360. {
  3361. if(length==0)return (offset<size()) ? offset : size();
  3362. if(length<=size())
  3363. {
  3364. CChar8 *u=c_str()+(offset<size()-length ? offset : size()-length);
  3365. for(; ; --u)
  3366. if(*u==*s && wmemcmp(u, s, length)==0)return u-c_str();
  3367. else if(u==c_str())break;
  3368. }
  3369. return npos;
  3370. }
  3371. std__string& append(const std__string& s, size_type offset, size_type length)
  3372. {
  3373. size_type left=s.size()-offset; if(left<length)length=left;
  3374. FREP(length)str+=s[offset+i];
  3375. return T;
  3376. }
  3377. std__string& append(CChar8 *s, size_type length)
  3378. {
  3379. if(_Inside(s))return append(T, s-c_str(), length);
  3380. FREP(length)str+=s[i];
  3381. return T;
  3382. }
  3383. std__string(CChar8 *t, Int length) {FREP(length)str+=t[i];}
  3384. std__string(CChar *t, Int length) {FREP(length)str+=t[i];}
  3385. std__string(CChar8 *t) : str(t) {}
  3386. std__string(CChar *t) : str(t) {}
  3387. std__string(C Str8 &s) : str(s) {}
  3388. std__string(C Str &s) : str(s) {}
  3389. std__string() {}
  3390. };
  3391. struct std__wstring
  3392. {
  3393. static INLINE int wmemcmp(CChar *a, CChar *b, size_type length) {for(; 0<length; ++a, ++b, --length)if(*a!=*b)return (*a<*b) ? -1 : +1; return 0;}
  3394. static INLINE CChar* wmemchr(CChar *t, CChar c, size_type length) {for(; 0<length; ++t, --length)if(*t== c)return t; return null;}
  3395. struct traits_type
  3396. {
  3397. static int compare(CChar *a, CChar *b, size_type length) {return wmemcmp(a, b, length);}
  3398. };
  3399. static const size_type npos=-1;
  3400. typedef Char value_type;
  3401. typedef size_t size_type;
  3402. typedef CChar* const_pointer;
  3403. typedef Char* iterator;
  3404. Str str;
  3405. std__wstring operator+( Char8 c)C {return str+c ;}
  3406. std__wstring operator+( Char c)C {return str+c ;}
  3407. std__wstring operator+(CChar8 *t)C {return str+t ;}
  3408. std__wstring operator+(CChar *t)C {return str+t ;}
  3409. std__wstring operator+(C Str8 &s)C {return str+s ;}
  3410. std__wstring operator+(C Str &s)C {return str+s ;}
  3411. std__wstring operator+(C std__wstring &s)C {return str+s.str;}
  3412. std__wstring& operator+=( Char8 c) {str+=c ; return T;}
  3413. std__wstring& operator+=( Char c) {str+=c ; return T;}
  3414. std__wstring& operator+=(CChar8 *t) {str+=t ; return T;}
  3415. std__wstring& operator+=(CChar *t) {str+=t ; return T;}
  3416. std__wstring& operator+=(C Str8 &s) {str+=s ; return T;}
  3417. std__wstring& operator+=(C Str &s) {str+=s ; return T;}
  3418. std__wstring& operator+=(C std__wstring &s) {str+=s.str; return T;}
  3419. bool operator==(CChar8 *t)C {return Equal(str, t , true);}
  3420. bool operator==(CChar *t)C {return Equal(str, t , true);}
  3421. bool operator==(C Str8 &s)C {return Equal(str, s , true);}
  3422. bool operator==(C Str &s)C {return Equal(str, s , true);}
  3423. bool operator==(C std__wstring &s)C {return Equal(str, s.str, true);}
  3424. bool operator!=(CChar8 *t)C {return !Equal(str, t , true);}
  3425. bool operator!=(CChar *t)C {return !Equal(str, t , true);}
  3426. bool operator!=(C Str8 &s)C {return !Equal(str, s , true);}
  3427. bool operator!=(C Str &s)C {return !Equal(str, s , true);}
  3428. bool operator!=(C std__wstring &s)C {return !Equal(str, s.str, true);}
  3429. iterator begin() {return iterator(c_str());}
  3430. iterator end () {return iterator(c_str()+size());}
  3431. Bool empty()C {return !str.is ();}
  3432. CChar* c_str()C {return str ();}
  3433. size_type size()C {return str.length();}
  3434. size_type length()C {return str.length();}
  3435. Char operator[](Int i )C {return str[i];}
  3436. void clear( ) {str.clear();}
  3437. void resize(int length) {if(length<=size())str.clip(length);else str.reserve(length);}
  3438. bool _Inside(CChar *s)C {if(!s || s<c_str() || c_str()+size()<=s)return false;else return true;}
  3439. std__wstring substr(size_type start, size_type length=npos)C {if(length==npos)length=T.size()-start; Str s; s.reserve(length); FREP(length)s+=T[start+i]; return s;}
  3440. int compare(size_type offset, size_type length, C std__wstring &s)C {return compare(offset, length, s, 0, npos);}
  3441. int compare(size_type offset, size_type length, C std__wstring &s, size_type offset2, size_type length2)C
  3442. {
  3443. if(s.size()-offset2<length2)length2=s.size()-offset2;
  3444. return compare(offset, length, s.c_str()+offset2, length2);
  3445. }
  3446. int compare(size_type offset, size_type length, CChar *s, size_type length2)C
  3447. {
  3448. if(size()-offset<length)length=size()-offset;
  3449. size_type c=wmemcmp(c_str()+offset, s, (length<length2) ? length : length2);
  3450. return (c ? (int)c : (length<length2) ? -1 : (length==length2) ? 0 : +1);
  3451. }
  3452. void swap(std__wstring &s) {Swap(T, s);}
  3453. size_type find(C std__wstring &s, size_type offset=0)C {return find(s.c_str(), offset, s.size() );}
  3454. size_type find( CChar *s, size_type offset=0)C {return find(s , offset, Length(s));}
  3455. size_type find( CChar *s, size_type offset, size_type length)C
  3456. {
  3457. if(length==0 && offset<=size())return offset;
  3458. size_type i;
  3459. if(offset<size() && length<=(i=size()-offset))
  3460. {
  3461. CChar *u, *v;
  3462. for(i-=length-1, v=c_str()+offset; u=wmemchr(v, *s, i); i-=u-v+1, v=u+1)
  3463. if(wmemcmp(u, s, length)==0)return u-c_str();
  3464. }
  3465. return npos;
  3466. }
  3467. size_type rfind(C std__wstring &s, size_type offset=0)C {return rfind(s.c_str(), offset, s.size() );}
  3468. size_type rfind( CChar *s, size_type offset=0)C {return rfind(s , offset, Length(s));}
  3469. size_type rfind( CChar *s, size_type offset, size_type length)C
  3470. {
  3471. if(length==0)return (offset<size()) ? offset : size();
  3472. if(length<=size())
  3473. {
  3474. CChar *u=c_str()+(offset<size()-length ? offset : size()-length);
  3475. for(; ; --u)
  3476. if(*u==*s && wmemcmp(u, s, length)==0)return u-c_str();
  3477. else if(u==c_str())break;
  3478. }
  3479. return npos;
  3480. }
  3481. std__wstring& append(const std__wstring& s, size_type offset, size_type length)
  3482. {
  3483. size_type left=s.size()-offset; if(left<length)length=left;
  3484. FREP(length)str+=s[offset+i];
  3485. return T;
  3486. }
  3487. std__wstring& append(CChar *s, size_type length)
  3488. {
  3489. if(_Inside(s))return append(T, s-c_str(), length);
  3490. FREP(length)str+=s[i];
  3491. return T;
  3492. }
  3493. std__wstring(CChar8 *t, Int length) {FREP(length)str+=t[i];}
  3494. std__wstring(CChar *t, Int length) {FREP(length)str+=t[i];}
  3495. std__wstring(CChar8 *t) : str(t) {}
  3496. std__wstring(CChar *t) : str(t) {}
  3497. std__wstring(C Str8 &s) : str(s) {}
  3498. std__wstring(C Str &s) : str(s) {}
  3499. std__wstring() {}
  3500. };
  3501. // Include Google Diff Match Patch Library
  3502. } // close EE namespace to include "std" headers in global namespace
  3503. #undef C
  3504. #undef DELETE
  3505. // don't include unwanted headers
  3506. //#define _STDEXCEPT_
  3507. #define _ITERATOR_
  3508. #define _MEMORY_
  3509. #define _STRING_
  3510. #define _INC_STRING
  3511. #define _XSTRING_
  3512. #if WINDOWS
  3513. #include <xmemory>
  3514. #endif
  3515. namespace std // use custom strings to avoid linker error on VS2010 when using VS2008 lib
  3516. {
  3517. typedef std__string string;
  3518. typedef std__wstring wstring;
  3519. }
  3520. #else
  3521. } // close EE namespace to include "std" headers in global namespace
  3522. #include <string>
  3523. typedef std:: string std__string ;
  3524. typedef std::wstring std__wstring;
  3525. #endif
  3526. // changes:
  3527. // std::vector -> std__vector
  3528. // std::wcstol -> TextInt (because of Android compile errors)
  3529. #include "Libs/Diff Match Patch Old/diff_match_patch.h"
  3530. #define C const
  3531. namespace EE{
  3532. Str Merge(C Str &base, C Str &a, C Str &b, MemPtr<TextPatch> patches, Int timeout)
  3533. {
  3534. std__wstring Base=(base ? base() : u""), A=(a ? a() : u""), B=(b ? b() : u""); // std::string crashes on null
  3535. diff_match_patch<std__wstring> dmp; dmp.Diff_Timeout=((timeout<=0) ? 0.0f : timeout/1000.0f);
  3536. diff_match_patch<std__wstring>::Patches dmp_patches=dmp.patch_make(Base, A);
  3537. std::pair<std__wstring, std__vector<bool> > out=dmp.patch_apply(dmp_patches, B);
  3538. if(patches)
  3539. {
  3540. patches.setNum(Min((Int)dmp_patches.size(), out.second.size()));
  3541. std::list<diff_match_patch<std__wstring>::Patch>::iterator start=dmp_patches.begin();
  3542. FREPA(patches)
  3543. {
  3544. diff_match_patch<std__wstring>::Patch &dmp_patch=*start++;
  3545. TextPatch & patch= patches[i];
  3546. patch.ok=out.second[i];
  3547. patch.base_offset=dmp_patch.start1; patch.base_length=dmp_patch.length1;
  3548. patch. a_offset=dmp_patch.start2; patch. a_length=dmp_patch.length2;
  3549. patch.diffs.setNum((Int)dmp_patch.diffs.size());
  3550. std::list<diff_match_patch<std__wstring>::Diff>::iterator start=dmp_patch.diffs.begin();
  3551. FREPA(patch.diffs)
  3552. {
  3553. diff_match_patch<std__wstring>::Diff &dmp_diff=*start++;
  3554. TextPatch::Diff & diff=patch.diffs[i];
  3555. diff.text=dmp_diff.text.c_str();
  3556. switch(dmp_diff.operation)
  3557. {
  3558. case diff_match_patch<std__wstring>::DELETE: diff.mode=TextPatch::DEL ; break;
  3559. case diff_match_patch<std__wstring>::INSERT: diff.mode=TextPatch::ADD ; break;
  3560. default : diff.mode=TextPatch::EQUAL; break;
  3561. }
  3562. }
  3563. }
  3564. }
  3565. return out.first.c_str();
  3566. }
  3567. #endif
  3568. /******************************************************************************/
  3569. Bool Error (C Str &msg ) {Gui.msgBox(S, msg); return false;}
  3570. Bool ErrorDel (C Str &file ) {return Error(S+"Error deleting\n\""+ file+'"');}
  3571. Bool ErrorRead (C Str &file ) {return Error(S+"Error accessing\n\""+ file+'"');}
  3572. Bool ErrorWrite(C Str &file ) {return Error(S+"Error writing to\n\""+file+'"');}
  3573. Bool ErrorCopy (C Str &src, C Str &dest) {return Error(S+"Error copying\n\""+src+"\"\nto\n\""+dest+'"');}
  3574. Bool ErrorMove (C Str &src, C Str &dest) {return Error(S+"Error moving\n\""+src+"\"\nto\n\""+dest+'"');}
  3575. /******************************************************************************
  3576. It's important to note, that Code Signing does not modify section offsets, sizes or data.
  3577. /******************************************************************************/
  3578. #if WINDOWS_OLD // create a Hash section for verification purposes
  3579. #define EE_SECTION_NAME "Hash" // name will be clamped to 8 chars
  3580. #pragma data_seg(EE_SECTION_NAME) // this will create a custom 'ExeSection'
  3581. ULong Hash=0; // have to specify a value (=0), because otherwise the section will not be created, and can't do "static" because it will be removed
  3582. #pragma data_seg()
  3583. static void Set(ExeSection &section, C IMAGE_SECTION_HEADER &img_section, Bool x64)
  3584. {
  3585. ASSERT(SIZE(img_section.Name)==8 && ELMS(img_section.Name)==8 && SIZE(section.name)==9);
  3586. CopyFast(section.name, img_section.Name, 8); section.name[8]='\0'; // !! Copy instead of Set because 'img_section.Name' may not be null-terminated !!
  3587. if( Equal(section.name, ".data", true))section.type=ExeSection::VARIABLE ;else
  3588. if( Equal(section.name, ".rdata", true)
  3589. || !x64 && Equal(section.name, ".text", true))section.type=ExeSection::CONST_PROCESS;else // most likely this changes because of virtual function table reallocation during EXE loading
  3590. if( Equal(section.name, EE_SECTION_NAME, true))section.type=ExeSection::HASH ;else // EE storage of hash
  3591. section.type=ExeSection::CONSTANT ;
  3592. }
  3593. #endif
  3594. Bool ParseProcess(MemPtr<ExeSection> sections)
  3595. {
  3596. #if WINDOWS_OLD
  3597. if(HINSTANCE hInstance=GetModuleHandle(null))
  3598. {
  3599. C IMAGE_DOS_HEADER *img_dos =(PIMAGE_DOS_HEADER)hInstance;
  3600. C IMAGE_NT_HEADERS *img_nt =(PIMAGE_NT_HEADERS)(UIntPtr(hInstance)+img_dos->e_lfanew);
  3601. C IMAGE_SECTION_HEADER *img_sections=(PIMAGE_SECTION_HEADER)(img_nt+1);
  3602. sections.setNum(img_nt->FileHeader.NumberOfSections); FREPA(sections)
  3603. {
  3604. C IMAGE_SECTION_HEADER &img_section=img_sections[i];
  3605. ExeSection &section=sections[i];
  3606. Set(section, img_section, X64);
  3607. section.offset=img_section.VirtualAddress+UIntPtr(hInstance);
  3608. section.size =img_section.Misc.VirtualSize;
  3609. }
  3610. return true;
  3611. }
  3612. #endif
  3613. sections.clear(); return false;
  3614. }
  3615. Bool ParseExe(File &f, MemPtr<ExeSection> sections)
  3616. {
  3617. #if WINDOWS_OLD
  3618. #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
  3619. #define IMAGE_NT_SIGNATURE 0x00004550 // PE00
  3620. ULong offset=f.pos();
  3621. IMAGE_DOS_HEADER img_dos; f>>img_dos;
  3622. if(img_dos.e_magic==IMAGE_DOS_SIGNATURE)
  3623. if(f.pos(img_dos.e_lfanew))
  3624. if(f.getUInt()==IMAGE_NT_SIGNATURE)
  3625. {
  3626. IMAGE_FILE_HEADER img_file; f>>img_file;
  3627. switch(img_file.Machine)
  3628. {
  3629. case IMAGE_FILE_MACHINE_AMD64: {IMAGE_OPTIONAL_HEADER64 img_optional; f>>img_optional;} break;
  3630. case IMAGE_FILE_MACHINE_I386 : {IMAGE_OPTIONAL_HEADER32 img_optional; f>>img_optional;} break;
  3631. default : goto error;
  3632. }
  3633. if(f.ok())
  3634. {
  3635. Bool x64=(img_file.Machine==IMAGE_FILE_MACHINE_AMD64);
  3636. sections.setNum(img_file.NumberOfSections);
  3637. FREPA(sections)
  3638. {
  3639. IMAGE_SECTION_HEADER img_section; f>>img_section;
  3640. ExeSection &section=sections[i];
  3641. Set(section, img_section, x64);
  3642. section.offset=img_section.PointerToRawData+offset;
  3643. section.size =img_section.Misc.VirtualSize;
  3644. }
  3645. if(f.ok())return true;
  3646. }
  3647. }
  3648. error:;
  3649. #endif
  3650. sections.clear(); return false;
  3651. }
  3652. Bool ParseExe(C Str &name, MemPtr<ExeSection> sections)
  3653. {
  3654. File f; if(f.readTry(name))return ParseExe(f, sections);
  3655. sections.clear(); return false;
  3656. }
  3657. Int FindSectionNameI (C MemPtr<ExeSection> &sections, CChar8 *name ) {REPA(sections)if(Equal(sections[i].name, name, true))return i; return -1;}
  3658. Int FindSectionOffsetI(C MemPtr<ExeSection> &sections, CPtr offset) {REPA(sections)if( sections[i].contains(offset ))return i; return -1;}
  3659. C ExeSection* FindSectionName (C MemPtr<ExeSection> &sections, CChar8 *name ) {return sections.addr(FindSectionNameI (sections, name ));}
  3660. C ExeSection* FindSectionOffset (C MemPtr<ExeSection> &sections, CPtr offset) {return sections.addr(FindSectionOffsetI(sections, offset));}
  3661. /******************************************************************************/
  3662. void InitMisc()
  3663. {
  3664. #if MAC
  3665. PasteboardCreate(kPasteboardClipboard, &Pasteboard);
  3666. #endif
  3667. #if DEBUG
  3668. TestCyclic();
  3669. for(Int range=1; range<=8; range++)
  3670. {
  3671. Flt power=0; for(Int i= 0; i<=range; i++)power+=BlendSmoothCube(i/Flt(range)); DYNAMIC_ASSERT(Equal(power, BlendSmoothCubeSumHalf(range)), "BlendSmoothCubeSumHalf");
  3672. power=0; for(Int i=-range; i<=range; i++)power+=BlendSmoothCube(i/Flt(range)); DYNAMIC_ASSERT(Equal(power, BlendSmoothCubeSum (range)), "BlendSmoothCubeSum");
  3673. }
  3674. #endif
  3675. }
  3676. /******************************************************************************/
  3677. #if APPLE
  3678. Boolean GetDictionaryBoolean(CFDictionaryRef dict, const void *key)
  3679. {
  3680. if(dict)if(CFBooleanRef ref=(CFBooleanRef)CFDictionaryGetValue(dict, key))return CFBooleanGetValue(ref);
  3681. return false;
  3682. }
  3683. long GetDictionaryLong(CFDictionaryRef dict, const void *key)
  3684. {
  3685. long value=0; if(dict)if(CFNumberRef ref=(CFNumberRef)CFDictionaryGetValue(dict, key))CFNumberGetValue(ref, kCFNumberLongType, &value);
  3686. return value;
  3687. }
  3688. #endif
  3689. /******************************************************************************/
  3690. }
  3691. /******************************************************************************/