ExtAssetData.cpp 271 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928
  1. // Copyright 2017-2021 marynate. All Rights Reserved.
  2. #include "ExtAssetData.h"
  3. #include "ExtContentBrowser.h"
  4. #include "ExtContentBrowserSingleton.h"
  5. #include "ExtPackageUtils.h"
  6. #include "ExtContentBrowserUtils.h"
  7. #include "SExtContentBrowser.h"
  8. #include "SContentBrowserPathPicker.h"
  9. #include "HAL/FileManager.h"
  10. #include "Misc/ConfigCacheIni.h"
  11. #include "Misc/PackageName.h"
  12. #include "Misc/ScopedSlowTask.h"
  13. #include "AssetRegistry/AssetRegistryState.h"
  14. #include "AssetRegistry/AssetDataTagMap.h"
  15. #include "Engine/AssetManager.h"
  16. #include "Engine/Blueprint.h"
  17. #include "Engine/UserDefinedStruct.h"
  18. #include "Engine/UserDefinedEnum.h"
  19. #include "AssetRegistry/ARFilter.h"
  20. #include "ObjectTools.h"
  21. #include "Blueprint/BlueprintSupport.h"
  22. #include "Framework/Application/SlateApplication.h"
  23. #include "ContentBrowserModule.h"
  24. #include "UObject/UObjectIterator.h"
  25. #include "HAL/RunnableThread.h"
  26. #include "PackageTools.h"
  27. #include "AssetToolsModule.h"
  28. #include "FileHelpers.h"
  29. #include "UObject/CoreRedirects.h"
  30. #include "Misc/RedirectCollector.h"
  31. #include "Serialization/ArchiveReplaceObjectRef.h"
  32. #include "Engine/MapBuildDataRegistry.h"
  33. #include "Engine/PrimaryAssetLabel.h"
  34. #include "Async/Async.h"
  35. #include "Interfaces/IMainFrameModule.h"
  36. #include "CollectionManagerTypes.h"
  37. #include "CollectionManagerModule.h"
  38. #include "ICollectionManager.h"
  39. #include "DesktopPlatformModule.h"
  40. #include "FileUtilities/ZipArchiveWriter.h"
  41. #include "HAL/PlatformFilemanager.h"
  42. #include "Misc/FileHelper.h"
  43. #include "CollectionManagerModule.h"
  44. #include "InstancedReferenceSubobjectHelper.h"
  45. #include "Kismet2/BlueprintEditorUtils.h"
  46. #include "UObject/MetaData.h"
  47. #include "IContentBrowserDataModule.h"
  48. // Serialization
  49. #include "AssetRegistry/AssetDataTagMapSerializationDetails.h"
  50. #include "Serialization/LargeMemoryWriter.h"
  51. #include "Serialization/ArchiveProxy.h"
  52. #include "UObject/NameBatchSerialization.h"
  53. #if ECB_WIP_IMPORT_FOLDER_COLOR_OVERRIDE
  54. #include "AssetViewUtils.h"
  55. #endif
  56. // Drag and Drop
  57. #include "Input/DragAndDrop.h"
  58. #include "LevelEditorViewport.h"
  59. #define LOCTEXT_NAMESPACE "ExtContentBrowser"
  60. const FGuid FExtAssetRegistryVersion::GUID(0xDC449DF5, 0x817F4F19, 0x899A33C9, 0x0AB230B4);
  61. FCustomVersionRegistration GRegisterExtAssetRegistryCustomVersion(FExtAssetRegistryVersion::GUID, FExtAssetRegistryVersion::LatestVersion, TEXT("ExtAssetRegistryVer"));
  62. FString FExtAssetSupport::AssetPackageExtension = TEXT(".uasset");
  63. FString FExtAssetSupport::MapPackageExtension = TEXT(".umap");
  64. FString FExtAssetSupport::TextAssetPackageExtension = TEXT(".utxt");
  65. FString FExtAssetSupport::TextMapPackageExtension = TEXT(".utxtmap");
  66. namespace FPathsUtil
  67. {
  68. FString WithEndSlash(const FString& InPath)
  69. {
  70. FString PathWithEndSlash(InPath);
  71. FPaths::NormalizeDirectoryName(PathWithEndSlash);
  72. PathWithEndSlash.Append(TEXT("/"));
  73. return PathWithEndSlash;
  74. }
  75. bool IsSubOrSamePath(const FString& InSubPath, const FString& InRootPath)
  76. {
  77. FString SubPath(InSubPath);
  78. FPaths::NormalizeDirectoryName(SubPath);
  79. FString RootPath(InRootPath);
  80. FPaths::NormalizeDirectoryName(RootPath);
  81. if (SubPath.Equals(RootPath))
  82. {
  83. return true;
  84. }
  85. RootPath.Append(TEXT("/"));
  86. if (SubPath.StartsWith(RootPath))
  87. {
  88. return true;
  89. }
  90. return false;
  91. }
  92. bool IsFileInDir(const FString& InFile, const FString& InRootPath)
  93. {
  94. FString RootPath = WithEndSlash(InRootPath);
  95. return (InFile.StartsWith(RootPath));
  96. }
  97. // return: num of paths been merged
  98. int32 SortAndMergeDirs(const TArray<FString>& InPathsToCombine, TArray<FString>& OutCombinedPaths)
  99. {
  100. TArray<FString> PathsToCombine = InPathsToCombine;
  101. PathsToCombine.Sort();
  102. const int32 NumPaths = PathsToCombine.Num();
  103. TArray<int32> IndicesToRemove;
  104. for (int32 IndexA = 0; IndexA < NumPaths - 1; ++IndexA)
  105. {
  106. FString& A = PathsToCombine[IndexA];
  107. for (int32 IndexB = NumPaths - 1; IndexB > IndexA; --IndexB)
  108. {
  109. if (IndicesToRemove.Contains(IndexB))
  110. {
  111. continue;
  112. }
  113. FString& B = PathsToCombine[IndexB];
  114. //if (B.StartsWith(A, ESearchCase::IgnoreCase)) // todo: Mac -> Casesensitive
  115. if (FPathsUtil::IsSubOrSamePath(B, A))
  116. {
  117. IndicesToRemove.Add(IndexB);
  118. }
  119. }
  120. }
  121. if (IndicesToRemove.Num() > 0)
  122. {
  123. for (int32 Index = 0; Index < IndicesToRemove.Num(); ++Index)
  124. {
  125. PathsToCombine.RemoveAt(IndicesToRemove[Index]);
  126. }
  127. }
  128. OutCombinedPaths = PathsToCombine;
  129. return IndicesToRemove.Num();
  130. }
  131. }
  132. namespace FExtAssetCoreUtil
  133. {
  134. bool FExtAssetFeedbackContext::ReceivedUserCancel()
  135. {
  136. if (!bTaskWasCancelledCache)
  137. {
  138. bTaskWasCancelledCache = FExtFeedbackContextEditor::ReceivedUserCancel();
  139. }
  140. return bTaskWasCancelledCache;
  141. }
  142. ///////////////////////////////////////////
  143. FExtAssetWorkReporter::FExtAssetWorkReporter(const TSharedPtr<IExtAssetProgressReporter>& InReporter, const FText& InDescription, float InAmountOfWork, float InIncrementOfWork, bool bInterruptible)
  144. : Reporter(InReporter)
  145. , DefaultIncrementOfWork(InIncrementOfWork)
  146. {
  147. if (Reporter.IsValid())
  148. {
  149. Reporter->BeginWork(InDescription, InAmountOfWork, bInterruptible);
  150. }
  151. }
  152. FExtAssetWorkReporter::~FExtAssetWorkReporter()
  153. {
  154. if (Reporter.IsValid())
  155. {
  156. Reporter->EndWork();
  157. }
  158. }
  159. void FExtAssetWorkReporter::ReportNextStep(const FText& InMessage, float InIncrementOfWork)
  160. {
  161. if (Reporter.IsValid())
  162. {
  163. Reporter->ReportProgress(InIncrementOfWork, InMessage);
  164. }
  165. }
  166. void FExtAssetWorkReporter::ReportNextStep(const FText& InMessage)
  167. {
  168. ReportNextStep(InMessage, DefaultIncrementOfWork);
  169. }
  170. bool FExtAssetWorkReporter::IsWorkCancelled() const
  171. {
  172. if (Reporter.IsValid())
  173. {
  174. return Reporter->IsWorkCancelled();
  175. }
  176. return false;
  177. }
  178. ///////////////////////////////////////////
  179. void FExtAssetProgressUIReporter::BeginWork(const FText& InTitle, float InAmountOfWork, bool bInterruptible/* = true*/)
  180. {
  181. ProgressTasks.Emplace(new FScopedSlowTask(InAmountOfWork, InTitle, true, FeedbackContext.IsValid() ? *FeedbackContext.Get() : *GWarn));
  182. ProgressTasks.Last()->MakeDialog(bInterruptible);
  183. }
  184. void FExtAssetProgressUIReporter::EndWork()
  185. {
  186. if (ProgressTasks.Num() > 0)
  187. {
  188. ProgressTasks.Pop();
  189. }
  190. }
  191. void FExtAssetProgressUIReporter::ReportProgress(float Progress, const FText& InMessage)
  192. {
  193. if (ProgressTasks.Num() > 0)
  194. {
  195. TSharedPtr<FScopedSlowTask>& ProgressTask = ProgressTasks.Last();
  196. ProgressTask->EnterProgressFrame(Progress, InMessage);
  197. }
  198. }
  199. bool FExtAssetProgressUIReporter::IsWorkCancelled()
  200. {
  201. if (!bIsCancelled && ProgressTasks.Num() > 0)
  202. {
  203. const TSharedPtr<FScopedSlowTask>& ProgressTask = ProgressTasks.Last();
  204. bIsCancelled |= ProgressTask->ShouldCancel();
  205. }
  206. return bIsCancelled;
  207. }
  208. FFeedbackContext* FExtAssetProgressUIReporter::GetFeedbackContext() const
  209. {
  210. return FeedbackContext.IsValid() ? FeedbackContext.Get() : GWarn;
  211. }
  212. ///////////////////////////////////////////
  213. void FExtAssetProgressTextReporter::BeginWork(const FText& InTitle, float InAmountOfWork, bool bInterruptible/* = true*/)
  214. {
  215. ECB_LOG(Display, TEXT("Start: %s ..."), *InTitle.ToString());
  216. ++TaskDepth;
  217. }
  218. void FExtAssetProgressTextReporter::EndWork()
  219. {
  220. if (TaskDepth > 0)
  221. {
  222. --TaskDepth;
  223. }
  224. }
  225. void FExtAssetProgressTextReporter::ReportProgress(float Progress, const FText& InMessage)
  226. {
  227. if (TaskDepth > 0)
  228. {
  229. ECB_LOG(Display, TEXT("Doing %s ..."), *InMessage.ToString());
  230. }
  231. }
  232. bool FExtAssetProgressTextReporter::IsWorkCancelled()
  233. {
  234. return false;
  235. }
  236. FFeedbackContext* FExtAssetProgressTextReporter::GetFeedbackContext() const
  237. {
  238. return FeedbackContext.Get();
  239. }
  240. }
  241. ////////////////////////////////////////////////////
  242. // FExtConfigUtil
  243. //
  244. struct FExtConfigUtil
  245. {
  246. static FString GetConfigDirByContentRoot(const FString& InAssetContentRoot)
  247. {
  248. static FString EmptyString("");
  249. FString ConfigDir = InAssetContentRoot / TEXT("../Saved/Config/") / ANSI_TO_TCHAR(FPlatformProperties::PlatformName());
  250. FPaths::CollapseRelativeDirectories(ConfigDir);
  251. if (IFileManager::Get().DirectoryExists(*ConfigDir))
  252. {
  253. return ConfigDir;
  254. }
  255. return EmptyString;
  256. }
  257. static bool LoadPathColors(const FString& InAssetContentRoot, const FString& InConfigDir, TMap<FName, FLinearColor>& OutPathColors, bool bRemapPackagePathToFilePath = true)
  258. {
  259. FString IniFile = InConfigDir / TEXT("EditorPerProjectUserSettings.ini");
  260. ECB_LOG(Display, TEXT("[LoadPathColors] in %s"), *IniFile);
  261. FConfigFile ConfigFile;
  262. if (FConfigCacheIni::LoadExternalIniFile(ConfigFile, TEXT("EditorPerProjectUserSettings"), TEXT(""), *InConfigDir, /*bIsBaseIniName*/false, /*Platform*/nullptr, /*bForceReload*/true, /*bWriteDestIni*/false))
  263. {
  264. if (const FConfigSection* Sec = ConfigFile.Find(TEXT("PathColor")))
  265. {
  266. ECB_LOG(Display, TEXT(" Found Sec in %s"), *IniFile);
  267. for (FConfigSectionMap::TConstIterator SecIt(*Sec); SecIt; ++SecIt)
  268. {
  269. ECB_LOG(Display, TEXT(" %s=%s"), *SecIt.Key().ToString(), *SecIt.Value().GetValue());
  270. const FString& PackageName = SecIt.Key().ToString();
  271. const FString& ColorString = SecIt.Value().GetValue();
  272. FLinearColor Color;
  273. if (Color.InitFromString(ColorString))
  274. {
  275. if (bRemapPackagePathToFilePath)
  276. {
  277. FString FolderPath;
  278. FString PackageRoot = FExtAssetDataUtil::GetPackageRootFromFullPackagePath(PackageName);
  279. if (FExtAssetDataUtil::RemapGamePackageToFullFilePath(PackageRoot, PackageName, InAssetContentRoot, FolderPath, /*bPackageIsFolder*/true))
  280. {
  281. ECB_LOG(Display, TEXT(" => %s=%s"), *FolderPath, *Color.ToString());
  282. OutPathColors.Add(*FolderPath, Color);
  283. }
  284. }
  285. else
  286. {
  287. OutPathColors.Add(*PackageName, Color);
  288. }
  289. }
  290. }
  291. return true;
  292. }
  293. }
  294. return false;
  295. }
  296. };
  297. /////////////////////////////////////////////////////////////
  298. // FExtAssetData implementation
  299. //
  300. FExtAssetData::FExtAssetData()
  301. : bParsed(false)
  302. , bValid(false)
  303. , bHasThumbnail(false)
  304. , bCompatible(false)
  305. {
  306. InvalidReason = EInvalidReason::NotParsed;
  307. AssetContentType = EContentType::Orphan;
  308. }
  309. FExtAssetData::FExtAssetData(const FString& InPackageFilePath, bool bDelayParse/* = false*/)
  310. : PackageFilePath(*InPackageFilePath)
  311. , bParsed(false)
  312. , bValid(false)
  313. , bHasThumbnail(false)
  314. , bCompatible(false)
  315. {
  316. InvalidReason = EInvalidReason::NotParsed;
  317. AssetContentType = EContentType::Orphan;
  318. if (PreParse() && !bDelayParse)
  319. {
  320. Parse();
  321. }
  322. }
  323. FExtAssetData::FExtAssetData(const UObject* InAsset, bool bAllowBlueprintClass)
  324. {
  325. #if ECB_TODO // todo: treat imported asset as external uasset package
  326. FString AssetPackageName = InAsset->GetOutermost()->GetFName();
  327. FString AssetFilePath = FPaths::ConvertRelativePathToFull(FPackageName::LongPackageNameToFilename(AssetPackageName)));
  328. PackageFilePath = *AssetFilePath;
  329. Parse();
  330. #endif
  331. }
  332. FExtAssetData::FExtAssetData(const FName& InPackageName)
  333. : InvalidReason(EInvalidReason::NotParsed)
  334. {
  335. if (FExtAssetData* CachedAssetData = FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetByPackageName(InPackageName))
  336. {
  337. *this = *CachedAssetData;
  338. return;
  339. }
  340. struct Local
  341. {
  342. static bool ConvertPackageNameToFilePathByAssetRegistry(const FName& InPackageName, FString& OutFullFilePath)
  343. {
  344. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  345. IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
  346. TArray<FAssetData> AssetDatas;
  347. AssetRegistry.GetAssetsByPackageName(*InPackageName.ToString(), AssetDatas, /*bIncludeOnlyOnDiskAssets*/true);
  348. if (AssetDatas.Num() > 0 && AssetDatas[0].IsValid())
  349. {
  350. FString FileExtension = FPackageName::GetAssetPackageExtension();
  351. if (AssetDatas[0].AssetClassPath.GetPackageName() == UWorld::StaticClass()->GetFName())
  352. {
  353. FileExtension = FPackageName::GetMapPackageExtension();
  354. }
  355. ECB_LOG(Display, TEXT("[ConvertTempPackageNameToFullFilePath] Packagename: %s, AssetDatas: %d, AssetClassPath: %s, Extension: %s"), *InPackageName.ToString(), AssetDatas.Num(), *AssetDatas[0].AssetClassPath.ToString(), *FileExtension);
  356. if (AssetDatas.Num() > 1)
  357. {
  358. ECB_LOG(Display, TEXT(" More than 1 asset data found. AssetDatas[1]: GetFullName: %s, AssetClassPath: %s"), *AssetDatas[1].GetFullName(), *AssetDatas[1].AssetClassPath.ToString());
  359. }
  360. FString RelativePackageFilePath;
  361. if (FPackageName::TryConvertLongPackageNameToFilename(AssetDatas[0].PackageName.ToString(), RelativePackageFilePath, FileExtension))
  362. {
  363. const FString AbsolutePackageFilePath = FPaths::ConvertRelativePathToFull(RelativePackageFilePath);
  364. OutFullFilePath = AbsolutePackageFilePath;
  365. ECB_LOG(Display, TEXT(" => FullFilePath: %s"), *AbsolutePackageFilePath);
  366. return true;
  367. }
  368. }
  369. return false;
  370. }
  371. };
  372. FString AbsolutePackageFilePath;
  373. if (Local::ConvertPackageNameToFilePathByAssetRegistry(InPackageName, AbsolutePackageFilePath))
  374. {
  375. FExtAssetData ExtAssetData(AbsolutePackageFilePath);
  376. *this = ExtAssetData;
  377. return;
  378. }
  379. PackageName = InPackageName;
  380. PackagePath = FName(*FPackageName::GetLongPackagePath(PackageName.ToString()));
  381. }
  382. bool FExtAssetData::PreParse()
  383. {
  384. bValid = true;
  385. // Normalize path
  386. FString NormalizedFilePath = PackageFilePath.ToString();
  387. // PackageFilePath
  388. {
  389. FPaths::NormalizeFilename(NormalizedFilePath);
  390. PackageFilePath = FName(*NormalizedFilePath);
  391. if (PackageFilePath == NAME_None)
  392. {
  393. bValid = false;
  394. InvalidReason = EInvalidReason::FilePathEmptyOrInvalid;
  395. }
  396. }
  397. const FString& FilePath = NormalizedFilePath;
  398. // PackageName, PackagePath, AssetName, AssetClass
  399. if (bValid)
  400. {
  401. FString TempPackageName = ConvertFilePathToTempPackageName(FilePath);
  402. PackageName = FName(*TempPackageName);
  403. if (FPackageName::DoesPackageNameContainInvalidCharacters(TempPackageName))
  404. {
  405. bValid = false;
  406. InvalidReason = EInvalidReason::FilePathContainsInvalidCharacter;
  407. }
  408. PackagePath = FName(*FPackageName::GetLongPackagePath(PackageName.ToString()));
  409. // AssetName Fallback in case it's an invalid uasset, for searching and display purpose
  410. AssetName = *FPaths::GetBaseFilename(FilePath);
  411. // ObjectPath Fallback
  412. FString ObjectPathStr;
  413. PackageName.AppendString(ObjectPathStr);
  414. ObjectPathStr.AppendChar('.');
  415. AssetName.AppendString(ObjectPathStr);
  416. ObjectPath = FName(*ObjectPathStr);
  417. // AssetClass Fallback
  418. AssetClass = NAME_None;
  419. }
  420. // Other Fallback
  421. AssetContentRoot = NAME_None;
  422. AssetRelativePath = NAME_None;
  423. AssetContentType = EContentType::Unknown;
  424. FileVersionUE4 = 0;
  425. AssetCount = 0;
  426. ThumbCount = 0;
  427. FileSize = 0;
  428. bHasThumbnail = false;;
  429. bCompatible = false;
  430. if (!bValid)
  431. {
  432. ECB_LOG(Warning, TEXT("PreParse %s failed. Failed Reason: %s"), *PackageFilePath.ToString(), *GetInvalidReason());
  433. bParsed = true;
  434. }
  435. return bValid;
  436. }
  437. bool FExtAssetData::DoParse()
  438. {
  439. bValid = true;
  440. const FString FilePath = PackageFilePath.ToString();
  441. // If disk file exist?
  442. {
  443. const bool bFileExist = IFileManager::Get().FileExists(*FilePath);
  444. if (!bFileExist)
  445. {
  446. bValid = false;
  447. InvalidReason = EInvalidReason::FileNotFound;
  448. }
  449. }
  450. // Parse AssetName, AssetClass, AssetRegistryTags, bHasThumbanil
  451. bValid = bValid && FExtPackageUtils::ParsePackageWithPackageReader(*this);
  452. if (bValid)
  453. {
  454. if (AssetCount < 1)
  455. {
  456. ECB_LOG(Display, TEXT("No Asset Data Found: %s"), *FilePath);
  457. bValid = false;
  458. InvalidReason = EInvalidReason::NoAssetData;
  459. }
  460. else if (AssetName == NAME_None)
  461. {
  462. ECB_LOG(Display, TEXT("AssetName is empty: %s"), *FilePath);
  463. bValid = false;
  464. InvalidReason = EInvalidReason::AssetNameEmpty;
  465. }
  466. else if (AssetClass == NAME_None)
  467. {
  468. ECB_LOG(Display, TEXT("AssetClass is empty: %s"), *FilePath);
  469. bValid = false;
  470. InvalidReason = EInvalidReason::ClassNameEmpty;
  471. }
  472. // Redirector is not supported (for now)
  473. else if (AssetClass == UObjectRedirector::StaticClass()->GetFName())
  474. {
  475. ECB_LOG(Display, TEXT("Redirctor found: %s"), *FilePath);
  476. #if !ECB_FEA_REDIRECTOR_SUPPORT
  477. bValid = false;
  478. InvalidReason = EInvalidReason::RedirectorNotSupported;
  479. #endif
  480. }
  481. // MapBuildDataRegistry is not supported
  482. #if ECB_DISABLE
  483. else if (AssetClass == UMapBuildDataRegistry::StaticClass()->GetFName()
  484. || AssetClass == UPrimaryAssetLabel::StaticClass()->GetFName())
  485. {
  486. bValid = false;
  487. InvalidReason = EInvalidReason::GenericAssetTypeNotSupported;
  488. }
  489. #endif
  490. // Class asset is not supported
  491. else if (AssetClass == NAME_Class)
  492. {
  493. ECB_LOG(Display, TEXT("Class asset found: %s"), *FilePath);
  494. bValid = false;
  495. InvalidReason = EInvalidReason::ClassAssetNotSupported;
  496. }
  497. }
  498. // ObjectPath
  499. if (bValid)
  500. {
  501. FString ObjectPathStr;
  502. PackageName.AppendString(ObjectPathStr);
  503. ObjectPathStr.AppendChar('.');
  504. AssetName.AppendString(ObjectPathStr);
  505. ObjectPath = FName(*ObjectPathStr);
  506. }
  507. // Compatibility
  508. if (bValid)
  509. {
  510. CheckIfCompatibleWithCurrentEngineVersion();
  511. if (!bCompatible)
  512. {
  513. bValid = false;
  514. InvalidReason = EInvalidReason::NotCompatiableWithCurrentEngineVersion;
  515. }
  516. }
  517. // Asset Content Root and Relative Path
  518. //if (bValid)
  519. {
  520. FString RelativePath;
  521. FString BaseDir;
  522. TSet<FName> AllDependencies;
  523. #if ECB_WIP_IMPORT_ORPHAN
  524. {
  525. AllDependencies.Append(HardDependentPackages);
  526. AllDependencies.Append(SoftReferencesList);
  527. }
  528. #endif
  529. if (FExtContentBrowserSingleton::GetAssetRegistry().ParseAssetContentRoot(FilePath, RelativePath, BaseDir, AssetContentType, &AllDependencies))
  530. {
  531. AssetContentRoot = *BaseDir;
  532. AssetRelativePath = *RelativePath;
  533. //AssetContentRootDirName = *FPaths::GetPathLeaf(BaseDir);
  534. //ECB_LOG(Display, TEXT("[Parse] %s relative to %s(%s)."), *RelativePath, *AssetContentRootDirName.ToString(), *BaseDir);
  535. }
  536. }
  537. if (!bValid)
  538. {
  539. ECB_LOG(Warning, TEXT("Parse %s failed. Failed Reason: %s"), *FilePath, *GetInvalidReason());
  540. }
  541. bParsed = true;; // treat invalid as not parsed (todo: re-parse when file changed)
  542. return bValid;
  543. }
  544. bool FExtAssetData::Parse()
  545. {
  546. return DoParse();
  547. }
  548. bool FExtAssetData::ReParse()
  549. {
  550. if (PreParse())
  551. {
  552. return Parse();
  553. }
  554. return false;
  555. }
  556. bool FExtAssetData::HasThumbnail() const
  557. {
  558. return bHasThumbnail;
  559. }
  560. bool FExtAssetData::HasContentRoot() const
  561. {
  562. return AssetContentRoot != NAME_None && AssetRelativePath != NAME_None;
  563. }
  564. bool FExtAssetData::HasValidThumbnail() const
  565. {
  566. FObjectThumbnail ObjectThumbnail;
  567. if (LoadThumbnail(ObjectThumbnail))
  568. {
  569. return ObjectThumbnail.GetImageWidth() > 0 && ObjectThumbnail.GetImageHeight() > 0 && ObjectThumbnail.GetCompressedDataSize() > 0;
  570. }
  571. return false;
  572. }
  573. bool FExtAssetData::CanImportFast() const
  574. {
  575. return IsValid() && IsCompatibleWithCurrentEngineVersion() && HasContentRoot();
  576. }
  577. bool FExtAssetData::LoadThumbnail(FObjectThumbnail& OutThumbnail) const
  578. {
  579. return FExtPackageUtils::LoadThumbnailWithPackageReader(*this, OutThumbnail);
  580. }
  581. #if ECB_LEGACY
  582. void FExtAssetData::LoadSoftReferences(TArray<FName>& OutSoftReferences) const
  583. {
  584. FExtPackageUtils::LoadSoftReferences(*this, OutSoftReferences);
  585. }
  586. #endif
  587. FString FExtAssetData::GetSavedEngineVersionForDisplay() const
  588. {
  589. return SavedByEngineVersion.ToString(EVersionComponent::Patch);
  590. }
  591. const FString& FExtAssetData::GetAssetContentTypeForDisplay() const
  592. {
  593. static const FString Project(TEXT("Project"));
  594. static const FString VaultCache(TEXT("VaultCache"));
  595. static const FString Plugin(TEXT("Plugin"));
  596. static const FString PartialProject(TEXT("PartialProject"));
  597. static const FString Orphan(TEXT("Orphan"));
  598. static const FString Unknown(TEXT("Unknown"));
  599. static const FString Empty(TEXT(""));
  600. if (AssetContentType == EContentType::Project)
  601. {
  602. return Project;
  603. }
  604. else if (AssetContentType == EContentType::VaultCache)
  605. {
  606. return VaultCache;
  607. }
  608. else if (AssetContentType == EContentType::Plugin)
  609. {
  610. return Plugin;
  611. }
  612. else if (AssetContentType == EContentType::PartialProject)
  613. {
  614. return PartialProject;
  615. }
  616. else if (AssetContentType == EContentType::Orphan)
  617. {
  618. return Orphan;
  619. }
  620. else if (AssetContentType == EContentType::Unknown)
  621. {
  622. return Unknown;
  623. }
  624. return Empty;
  625. }
  626. UClass* FExtAssetData::GetIconClass(bool* bOutIsClassType) const
  627. {
  628. if (bOutIsClassType)
  629. {
  630. *bOutIsClassType = false;
  631. }
  632. UClass* ResultAssetClass = FindObjectSafe<UClass>(nullptr, *AssetClass.ToString());
  633. if (!ResultAssetClass)
  634. {
  635. return nullptr;
  636. }
  637. if (ResultAssetClass == UClass::StaticClass())
  638. {
  639. if (bOutIsClassType)
  640. {
  641. *bOutIsClassType = true;
  642. }
  643. return FindObject<UClass>(nullptr, *AssetName.ToString());
  644. }
  645. if (ResultAssetClass->IsChildOf<UBlueprint>())
  646. {
  647. if (bOutIsClassType)
  648. {
  649. *bOutIsClassType = true;
  650. }
  651. // We need to use the asset data to get the parent class as the blueprint may not be loaded
  652. FString ParentClassName;
  653. if (!GetTagValue(FBlueprintTags::NativeParentClassPath, ParentClassName))
  654. {
  655. GetTagValue(FBlueprintTags::ParentClassPath, ParentClassName);
  656. }
  657. if (!ParentClassName.IsEmpty())
  658. {
  659. UObject* Outer = nullptr;
  660. ResolveName(Outer, ParentClassName, false, false);
  661. return FindObject<UClass>(nullptr, *ParentClassName);
  662. }
  663. }
  664. // Default to using the class for the asset type
  665. return ResultAssetClass;
  666. }
  667. FSoftObjectPath FExtAssetData::GetSoftObjectPath() const
  668. {
  669. if (IsTopLevelAsset())
  670. {
  671. return FSoftObjectPath(PackageName, AssetName, FString());
  672. }
  673. else
  674. {
  675. TStringBuilder<FName::StringBufferSize> Builder;
  676. AppendObjectPath(Builder);
  677. return FSoftObjectPath(Builder.ToView());
  678. }
  679. }
  680. FString FExtAssetData::GetFolderPath() const
  681. {
  682. return FPaths::GetPath(PackageFilePath.ToString());
  683. }
  684. FString FExtAssetData::ConvertFilePathToTempPackageName(const FString& InFilePath)
  685. {
  686. FString FilePath = InFilePath;
  687. FPaths::NormalizeFilename(FilePath);
  688. FilePath.ReplaceInline(TEXT(":"), TEXT(""));
  689. while (FilePath.Find(TEXT("//")) != INDEX_NONE)
  690. {
  691. FilePath.ReplaceInline(TEXT("//"), TEXT("/"));
  692. }
  693. FString TempPackageName = FPaths::Combine(FExtAssetContants::ExtTempPackagePath, *FPaths::GetPath(FilePath), *FPaths::GetBaseFilename(FilePath));
  694. FString SanitizedPackageName = ObjectTools::SanitizeInvalidChars(TempPackageName, INVALID_LONGPACKAGE_CHARACTERS);
  695. return SanitizedPackageName;
  696. }
  697. FString& FExtAssetData::GetSandboxPackagePath()
  698. {
  699. static FString SandboxPackagePath = FExtAssetContants::ExtSandboxPackagePath; // TEXT("/UAssetBrowser/Sandbox");
  700. return SandboxPackagePath;
  701. }
  702. FString& FExtAssetData::GetSandboxPackagePathWithSlash()
  703. {
  704. static FString SandboxPackagePathWithSlash = FExtAssetContants::ExtSandboxPackagePath + TEXT("/");
  705. return SandboxPackagePathWithSlash;
  706. }
  707. FString& FExtAssetData::GetSandboxDir()
  708. {
  709. const FString ProjectIntermediateDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectIntermediateDir());
  710. static FString SandboxDir = FPaths::Combine(ProjectIntermediateDir, TEXT("UAssetBrowser"), TEXT("Sandbox"));
  711. return SandboxDir;
  712. }
  713. FString& FExtAssetData::GetUAssetBrowserTempDir()
  714. {
  715. const FString ProjectIntermediateDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectIntermediateDir());
  716. static FString TempDir = FPaths::Combine(ProjectIntermediateDir, TEXT("UAssetBrowser"), TEXT("Temp"));
  717. return TempDir;
  718. }
  719. FString& FExtAssetData::GetImportSessionTempDir()
  720. {
  721. const FString ProjectIntermediateDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectIntermediateDir());
  722. static FString SessionTempDir = FPaths::Combine(ProjectIntermediateDir, TEXT("UAssetBrowser"), TEXT("Import"), TEXT("Sessions"));
  723. return SessionTempDir;
  724. }
  725. FString FExtAssetData::GetZipExportSessionTempDir()
  726. {
  727. const FString ProjectIntermediateDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectIntermediateDir());
  728. const FString SessionId = FGuid::NewGuid().ToString(EGuidFormats::Digits);
  729. FString SessionTempDir = FPaths::Combine(ProjectIntermediateDir, TEXT("UAssetBrowser"), TEXT("Export"), TEXT("Sessions"), SessionId);
  730. return SessionTempDir;
  731. }
  732. void FExtAssetData::CheckIfCompatibleWithCurrentEngineVersion()
  733. {
  734. bCompatible = FileVersionUE4 <= GPackageFileUEVersion.ToValue() && FEngineVersion::Current().IsCompatibleWith(CompatibleWithEngineVersion);
  735. }
  736. bool FExtAssetData::IsCompatibleWithCurrentEngineVersion() const
  737. {
  738. return bCompatible;
  739. }
  740. FString FExtAssetData::GetInvalidReason() const
  741. {
  742. switch (InvalidReason)
  743. {
  744. case EInvalidReason::Valid: return TEXT("Valid");
  745. case EInvalidReason::FileNotFound: return TEXT("File Not Found");
  746. case EInvalidReason::FilePathEmptyOrInvalid: return TEXT("Empty Or Invalid File Path");
  747. case EInvalidReason::FilePathContainsInvalidCharacter: return TEXT("File Path Contains Invalid Character");
  748. case EInvalidReason::FailedToLoadPackage: return TEXT("Failed To Load Package");
  749. case EInvalidReason::PackageFileVersionEmptyOrTooOld: return TEXT("File Version Empty Or Too Old");
  750. case EInvalidReason::PackageFileOrCustomVersionTooNew: return TEXT("File Version Too New");
  751. case EInvalidReason::PackageFileMalformed: return TEXT("File Malformed");
  752. case EInvalidReason::PackageFileCustomVersionMissing: return TEXT("File Custom Version Missing");
  753. case EInvalidReason::PackageFileFailedToLoad: return TEXT("File Failed ToLoad");
  754. case EInvalidReason::NoAssetData: return TEXT("AssetData Empty");
  755. case EInvalidReason::AssetNameEmpty: return TEXT("AssetName Empty");
  756. case EInvalidReason::ClassNameEmpty: return TEXT("ClassName Empty");
  757. case EInvalidReason::RedirectorNotSupported: return TEXT("Redirector Not Supported");
  758. case EInvalidReason::GenericAssetTypeNotSupported: return FString::Printf(TEXT("%s Not Supported"), *AssetClass.ToString());
  759. case EInvalidReason::TextForamtNotSupported: return TEXT("TextForamt Not Supported");
  760. case EInvalidReason::CookedPackageNotSupported: return TEXT("Cooked Package Not Supported");
  761. case EInvalidReason::NotCompatiableWithCurrentEngineVersion: return TEXT("Not Compatiable With Current Engine Version");
  762. default: return TEXT("Unknown invalid reason.");
  763. }
  764. }
  765. void FExtAssetData::PrintAssetData() const
  766. {
  767. ECB_LOG(Display, TEXT(" FExtAssetData for %s"), *PackageFilePath.ToString());
  768. ECB_LOG(Display, TEXT(" ============================="));
  769. ECB_LOG(Display, TEXT(" FileVersionUE4: %d"), FileVersionUE4);
  770. ECB_LOG(Display, TEXT(" SavedByEngineVersion: %s"), *SavedByEngineVersion.ToString());
  771. ECB_LOG(Display, TEXT(" CompatibleEngineVersion: %s"), *CompatibleWithEngineVersion.ToString());
  772. ECB_LOG(Display, TEXT(" PackageName: %s"), *PackageName.ToString());
  773. ECB_LOG(Display, TEXT(" PackagePath: %s"), *PackagePath.ToString());
  774. ECB_LOG(Display, TEXT(" ObjectPath: %s"), *ObjectPath.ToString());
  775. ECB_LOG(Display, TEXT(" AssetName: %s"), *AssetName.ToString());
  776. ECB_LOG(Display, TEXT(" AssetClass: %s"), *AssetClass.ToString());
  777. ECB_LOG(Display, TEXT(" AssetContentRootDir: %s"), *AssetContentRoot.ToString());
  778. ECB_LOG(Display, TEXT(" AssetRelativePath: %s"), *AssetRelativePath.ToString());
  779. ECB_LOG(Display, TEXT(" bParsed: %d"), bParsed);
  780. ECB_LOG(Display, TEXT(" bValid: %d"), bValid);
  781. ECB_LOG(Display, TEXT(" bCompatible: %d"), bCompatible);
  782. ECB_LOG(Display, TEXT(" TagsAndValues: %d"), TagsAndValues.Num());
  783. ECB_LOG(Display, TEXT(" -----------------------------"));
  784. int32 Index = 0;
  785. for (const auto& TagValue : TagsAndValues)
  786. {
  787. ECB_LOG(Display, TEXT(" %d) %s : %s"), Index, *TagValue.Key.ToString(), *TagValue.Value.AsString());
  788. Index++;
  789. }
  790. ECB_LOG(Display, TEXT(" HardDependentPackages: %d"), HardDependentPackages.Num());
  791. ECB_LOG(Display, TEXT(" -----------------------------"));
  792. Index = 0;
  793. for (const FName& HardDependent : HardDependentPackages)
  794. {
  795. ECB_LOG(Display, TEXT(" %d) %s"), Index, *HardDependent.ToString());
  796. Index++;
  797. }
  798. ECB_LOG(Display, TEXT(" SoftReferencesList: %d"), SoftReferencesList.Num());
  799. ECB_LOG(Display, TEXT(" -----------------------------"));
  800. Index = 0;
  801. for (const FName& SoftReference : SoftReferencesList)
  802. {
  803. ECB_LOG(Display, TEXT(" %d) %s"), Index, *SoftReference.ToString());
  804. Index++;
  805. }
  806. }
  807. bool FExtAssetData::IsUAsset() const
  808. {
  809. return PackageFilePath != NAME_None && PackageName != NAME_None && PackageName.ToString().StartsWith(FExtAssetContants::ExtTempPackagePath);
  810. }
  811. bool FExtAssetData::IsTopLevelAsset() const
  812. {
  813. #if WITH_EDITORONLY_DATA
  814. if (OptionalOuterPath.IsNone())
  815. {
  816. // If no outer path, then path is PackageName.AssetName so we must be top level
  817. return true;
  818. }
  819. TStringBuilder<FName::StringBufferSize> Builder;
  820. AppendObjectPath(Builder);
  821. int32 SubObjectIndex;
  822. FStringView(Builder).FindChar(SUBOBJECT_DELIMITER_CHAR, SubObjectIndex);
  823. return SubObjectIndex == INDEX_NONE;
  824. #else
  825. // Non-top-level assets only appear in the editor
  826. return true;
  827. #endif
  828. }
  829. bool FExtAssetData::IsUMap() const
  830. {
  831. return PackageFilePath != NAME_None && FPaths::GetExtension(PackageFilePath.ToString(), /*IncludeDot*/ true).Equals(FExtAssetSupport::MapPackageExtension, ESearchCase::IgnoreCase);
  832. }
  833. template <typename ValueType>
  834. bool FExtAssetData::GetTagValue(const FName InTagName, ValueType& OutTagValue) const
  835. {
  836. const FAssetDataTagMapSharedView::FFindTagResult FoundValue = TagsAndValues.FindTag(InTagName);
  837. if (FoundValue.IsSet())
  838. {
  839. FMemory::Memzero(&OutTagValue, sizeof(ValueType));
  840. LexFromString(OutTagValue, *FoundValue.GetValue());
  841. return true;
  842. }
  843. return false;
  844. }
  845. ///////////////////////////////////////////////////////////////////////////////////////////
  846. // FExtAssetRegistry
  847. FExtAssetRegistry::FExtAssetRegistry()
  848. {
  849. }
  850. FExtAssetRegistry::~FExtAssetRegistry()
  851. {
  852. Shutdown(); // Todo: should be called manually from module shutdown?
  853. }
  854. void FExtAssetRegistry::BroadcastAssetUpdatedEvent(const FExtAssetData& UpdatedAsset)
  855. {
  856. AssetUpdatedEvent.Broadcast(UpdatedAsset);
  857. }
  858. bool FExtAssetRegistry::GetAndTrimFolderGatherResult()
  859. {
  860. const bool bRefreshPathViewWhileScan = true; //todo: move to options maybe?
  861. bool bRootDirGathered = false;
  862. bool bSubDirGathered = false;
  863. if (BackgroundFolderGatherer.IsValid())
  864. {
  865. TArray<FExtFolderGatherResult> RootDirGatherResults;
  866. double RootDirGatherTime;
  867. TArray<FExtFolderGatherResult> SubDirGatherResults;
  868. TArray<FExtAssetContentRootGatherResult> AssetContentRootGatherResults;
  869. bRootDirGathered = BackgroundFolderGatherer->GetAndTrimGatherResult(RootDirGatherResults, RootDirGatherTime, SubDirGatherResults, AssetContentRootGatherResults);
  870. bSubDirGathered = SubDirGatherResults.Num() > 0;
  871. bool bAssetContentRootGathered = AssetContentRootGatherResults.Num() > 0;
  872. if (bAssetContentRootGathered)
  873. {
  874. for (const auto& GatherResult : AssetContentRootGatherResults)
  875. {
  876. const auto& AssetContentRoot = GatherResult.AssetContentRoot;
  877. const auto& AssetContentRootHost = GatherResult.AssetContentRootHost;
  878. const auto& AssetContentRootConfigDir = GatherResult.AssetContentRootConfigDir;
  879. const auto& AssetContentType = GatherResult.AssetContentType;
  880. AddAssetContentRoot(AssetContentRoot.ToString(), AssetContentRootHost.ToString(), AssetContentRootConfigDir.ToString(), AssetContentType);
  881. }
  882. }
  883. const bool bHasFolderGatheredToParse = bRootDirGathered || (bRefreshPathViewWhileScan && bSubDirGathered);
  884. if (bHasFolderGatheredToParse)
  885. {
  886. TArray<FExtFolderGatherResult>& GatherResults = bRootDirGathered ? RootDirGatherResults : SubDirGatherResults;
  887. for (const auto& GatherResult : GatherResults)
  888. {
  889. const auto& RoootFolder = GatherResult.RootPath;
  890. const auto& ParentFolder = GatherResult.ParentPath;
  891. const auto& SubFolders = GatherResult.SubPaths;
  892. const auto& RecurseSubFolders = GatherResult.RecurseSubPaths;
  893. const auto& FolderPakages = GatherResult.FolderPakages;
  894. if (bRootDirGathered)
  895. {
  896. ECB_LOG(Display, TEXT("BackgroundFolderGathered for %s, SubFolders: %d, RecurseSubFolders: %d, FolderPakages: %d"), *ParentFolder.ToString(), SubFolders.Num(), RecurseSubFolders.Num(), FolderPakages.Num());
  897. }
  898. else
  899. {
  900. //ECB_LOG(Display, TEXT("[Sub] BackgroundFolderGathered for %s, root: %s, %d, %d"), *ParentFolder.ToString(), *RoootFolder.ToString(), RecurseSubFolders.Num(), FolderPakages.Num());
  901. }
  902. if (!BackgroundGatheringFolders.Contains(RoootFolder))
  903. {
  904. //ECB_LOG(Display, TEXT(" => %s Removed. Skip gathered result."), *RoootFolder.ToString());
  905. continue;
  906. }
  907. {
  908. auto& CurrentCachedSubPaths = State.CachedSubPaths.FindOrAdd(ParentFolder);
  909. //if (CurrentCachedSubPaths.Num() != SubFolders.Num()) // already cached?
  910. {
  911. CurrentCachedSubPaths.Empty();
  912. CurrentCachedSubPaths.Append(SubFolders);
  913. }
  914. if (bRootDirGathered)
  915. {
  916. ECB_LOG(Display, TEXT(" => %d subfolders."), CurrentCachedSubPaths.Num());
  917. }
  918. }
  919. for (auto& Pair : RecurseSubFolders)
  920. {
  921. auto& Parent = Pair.Key;
  922. auto& Subs = Pair.Value;
  923. auto& CurrentCachedSubPaths = State.CachedSubPaths.FindOrAdd(Parent);
  924. //if (CurrentCachedSubPaths.Num() != SubFolders.Num()) // already cached?
  925. {
  926. CurrentCachedSubPaths.Empty();
  927. CurrentCachedSubPaths.Append(Subs);
  928. }
  929. }
  930. if (bRootDirGathered)
  931. {
  932. ECB_LOG(Display, TEXT(" => %d recursivley subfolders."), RecurseSubFolders.Num());
  933. }
  934. for (auto& Pair : FolderPakages)
  935. {
  936. State.CachedFilePathsByFolder.FindOrAdd(Pair.Key) = Pair.Value;
  937. }
  938. if (bRootDirGathered)
  939. {
  940. BackgroundGatheringFolders.Remove(ParentFolder);
  941. FolderFinishGatheringEvent.Broadcast(ParentFolder.ToString(), RoootFolder.ToString());
  942. }
  943. else
  944. {
  945. FolderFinishGatheringEvent.Broadcast(ParentFolder.ToString(), RoootFolder.ToString());
  946. BackgroundGatheringSubFolder = ParentFolder;
  947. }
  948. }
  949. if (bRootDirGathered)
  950. {
  951. ECB_LOG(Display, TEXT("# Background folder discovery finished in %0.4f seconds"), RootDirGatherTime);
  952. }
  953. }
  954. }
  955. //return bRefreshPathViewWhileScan ? (bMainGathered || bSubDirGathered) : bMainGathered;
  956. return bRootDirGathered;
  957. }
  958. bool FExtAssetRegistry::IsFolderBackgroundGathering(const FString& InFolder) const
  959. {
  960. return BackgroundGatheringFolders.Contains(*InFolder);
  961. }
  962. bool FExtAssetRegistry::GetAndTrimAssetGatherResult()
  963. {
  964. bool bHasGatherResult = false;
  965. if (BackgroundAssetGatherer.IsValid())
  966. {
  967. FExtAssetGatherResult GatherResult;
  968. double GatherTime = 0.;
  969. int32 PathsLeftInBatch = 0;
  970. bHasGatherResult = BackgroundAssetGatherer->GetAndTrimGatherResult(GatherResult, GatherTime, PathsLeftInBatch);
  971. if (bHasGatherResult)
  972. {
  973. const TMap<FName, FExtAssetData>& ParsedAssets = GatherResult.GetParsedAssets();
  974. for (const auto& Pair : ParsedAssets)
  975. {
  976. const FName& FilePath = Pair.Key;
  977. const FExtAssetData& AssetData = Pair.Value;
  978. ECB_LOG(Display, TEXT("BackgroundAssetGatherer for %s, bIsValide?: %d"), *FilePath.ToString(), AssetData.IsValid());
  979. FExtAssetData** FoundAssetData = State.CachedAssetsByFilePath.Find(FilePath);
  980. const bool bAlreadyCached = FoundAssetData != nullptr;
  981. bool bNeedBrodcast = false;
  982. if (!bAlreadyCached)
  983. {
  984. FExtAssetData* NewAssetData = new FExtAssetData(AssetData);
  985. State.CacheNewAsset(NewAssetData);
  986. // todo: Update CachedAssetsByFolder, move to CacheNewAsset?
  987. // {
  988. // const FString Dir = NewAssetData->GetFolderPath();
  989. // ECB_LOG(Display, TEXT(" => Dir: %s."), *Dir);
  990. //
  991. // TArray<FExtAssetData*>& CachedAssets = State.CachedAssetsByFolder.FindOrAdd(*Dir);
  992. // CachedAssets.AddUnique(NewAssetData);
  993. // }
  994. ECB_LOG(Display, TEXT(" => Cache as New Asset. %s"), *NewAssetData->PackageName.ToString());
  995. AssetGatheredEvent.Broadcast(*NewAssetData, PathsLeftInBatch);
  996. }
  997. else if (!(*FoundAssetData)->IsParsed())
  998. {
  999. *(*FoundAssetData) = AssetData;
  1000. State.ReCacheParsedAsset(*FoundAssetData);
  1001. ECB_LOG(Display, TEXT(" => ReCacheParsedAsset. %s"), *(*FoundAssetData)->PackageName.ToString());
  1002. AssetGatheredEvent.Broadcast(**FoundAssetData, PathsLeftInBatch);
  1003. }
  1004. }
  1005. if (PathsLeftInBatch == 0)
  1006. {
  1007. ECB_LOG(Display, TEXT("# Background asset discovery finished in %0.4f seconds"), GatherTime);
  1008. }
  1009. }
  1010. if (bHasGatherResult/*Sth. Gathered*/ || (bIsGatheringAssets && !bHasGatherResult) /*Was Gathering*/)
  1011. {
  1012. const FAssetGatherProgressUpdateData ProgressUpdateData(
  1013. NumToGatherAssets/*NumFilteredAssets*/, // NumTotalAssets
  1014. NumToGatherAssets - PathsLeftInBatch, // NumAssetsProcessedByAssetRegistry
  1015. 0, // NumAssetsPendingDataLoad
  1016. false/*bIsGatheringAssets*/ // bIsDiscoveringAssetFiles
  1017. );
  1018. AssetGatherProgressUpdatedEvent.Broadcast(ProgressUpdateData);
  1019. ECB_LOG(Display, TEXT(" AssetGatherProgressUpdatedEvent => NumToGatherAssets: %d, PathsLeftInBatch: %d, bIsGatheringAssets: %d")
  1020. , NumToGatherAssets, PathsLeftInBatch, bIsGatheringAssets);
  1021. }
  1022. bIsGatheringAssets = PathsLeftInBatch != 0;
  1023. }
  1024. return bHasGatherResult;
  1025. }
  1026. bool FExtAssetRegistry::GetAssets(const struct FARFilter& InFilter, TArray<FExtAssetData>& OutAssetData)
  1027. {
  1028. double GetAssetsStartTime = FPlatformTime::Seconds();
  1029. if (!IsFilterValid(InFilter, /*bAllowRecursion = */ true) || InFilter.IsEmpty())
  1030. {
  1031. return false;
  1032. }
  1033. // Expand recursion on filter
  1034. FARFilter Filter;
  1035. ExpandRecursiveFilter(InFilter, Filter);
  1036. // Verify filter input. If all assets are needed, use GetAllAssets() instead.
  1037. if (!IsFilterValid(Filter, /*bAllowRecursion = */ false) || InFilter.IsEmpty())
  1038. {
  1039. return false;
  1040. }
  1041. // Prepare a set of each filter component for fast searching
  1042. TSet<FName> FilterPackagePaths(Filter.PackagePaths);
  1043. TSet<FTopLevelAssetPath> FilterClassPaths(Filter.ClassPaths);
  1044. TSet<FName> FilterPackageNames(Filter.PackageNames);
  1045. // Form a set of assets matched by each filter
  1046. TArray<TSet<FExtAssetData*>> DiskFilterSets;
  1047. bool bCanceledInFilterByPathsPhase = false;
  1048. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  1049. const bool bIgnoreInvalidAssets = !ExtContentBrowserSetting->DisplayInvalidAssets;
  1050. // On disk package paths
  1051. if (FilterPackagePaths.Num())
  1052. {
  1053. TSet<FExtAssetData*>& PathFilter = DiskFilterSets[DiskFilterSets.AddDefaulted()];
  1054. {
  1055. TArray<FExtAssetData*> FilteredAssets;
  1056. for (FName PackagePath : FilterPackagePaths)
  1057. {
  1058. const TArray<FExtAssetData*> PathAssets = GetCachedAssetsByFolder(PackagePath);
  1059. if (PathAssets.Num() > 0)
  1060. {
  1061. for (FExtAssetData* AssetData : PathAssets)
  1062. {
  1063. #if ECB_FEA_SHOW_INVALID
  1064. const bool bShouldIgnore = bIgnoreInvalidAssets && !AssetData->IsValid();
  1065. #else
  1066. const bool bShouldIgnore = !AssetData->IsValid();
  1067. #endif
  1068. if (!bShouldIgnore)
  1069. {
  1070. FilteredAssets.Add(AssetData);
  1071. }
  1072. }
  1073. }
  1074. }
  1075. PathFilter.Append(FilteredAssets);
  1076. }
  1077. }
  1078. // On disk classes
  1079. if (FilterClassPaths.Num())
  1080. {
  1081. TSet<FExtAssetData*>& ClassFilter = DiskFilterSets[DiskFilterSets.AddDefaulted()];
  1082. TArray<FExtAssetData*> FilteredAssets;
  1083. for (FTopLevelAssetPath ClassPath : FilterClassPaths)
  1084. {
  1085. const TSet<FName>& InThosePaths = FilterPackagePaths;
  1086. FName ClassName = ClassPath.GetAssetName();
  1087. const TArray<FExtAssetData*> ClassAssets = GetCachedAssetsByClass(ClassName);
  1088. if (ClassAssets.Num() > 0)
  1089. {
  1090. for (FExtAssetData* AssetData : ClassAssets)
  1091. {
  1092. #if ECB_FEA_SHOW_INVALID
  1093. const bool bShouldIgnore = bIgnoreInvalidAssets && !AssetData->IsValid();
  1094. #else
  1095. const bool bShouldIgnore = !AssetData->IsValid();
  1096. #endif
  1097. if (!bShouldIgnore)
  1098. {
  1099. FilteredAssets.Add(AssetData);
  1100. }
  1101. }
  1102. }
  1103. }
  1104. ClassFilter.Append(FilteredAssets);
  1105. }
  1106. // On disk package names
  1107. if (FilterPackageNames.Num())
  1108. {
  1109. TSet<FExtAssetData*>& PackageNameFilter = DiskFilterSets[DiskFilterSets.AddDefaulted()];
  1110. for (FName PackageName : FilterPackageNames)
  1111. {
  1112. const FName* FilePath = State.CachedPackageNameToFilePathMap.Find(PackageName);
  1113. if (FilePath != nullptr)
  1114. {
  1115. FExtAssetData* AssetData = GetCachedAssetByFilePath(*FilePath);
  1116. if (AssetData/* && AssetData->IsValid()*/)
  1117. {
  1118. PackageNameFilter.Add(AssetData);
  1119. }
  1120. }
  1121. }
  1122. }
  1123. // If we have any filter sets, add the assets which are contained in the sets to OutAssetData
  1124. if (DiskFilterSets.Num() > 0)
  1125. {
  1126. // Initialize the combined filter set to the first set, in case we can skip combining.
  1127. TSet<FExtAssetData*>* CombinedFilterSetPtr = &DiskFilterSets[0];
  1128. TSet<FExtAssetData*> IntersectedFilterSet;
  1129. // If we have more than one set, we must combine them. We take the intersection
  1130. if (DiskFilterSets.Num() > 1)
  1131. {
  1132. IntersectedFilterSet = *CombinedFilterSetPtr;
  1133. CombinedFilterSetPtr = &IntersectedFilterSet;
  1134. for (int32 SetIdx = 1; SetIdx < DiskFilterSets.Num() && IntersectedFilterSet.Num() > 0; ++SetIdx)
  1135. {
  1136. // If the other set is smaller, swap it so we iterate the smaller set
  1137. TSet<FExtAssetData*> OtherFilterSet = DiskFilterSets[SetIdx];
  1138. if (OtherFilterSet.Num() < IntersectedFilterSet.Num())
  1139. {
  1140. Swap(OtherFilterSet, IntersectedFilterSet);
  1141. }
  1142. for (auto It = IntersectedFilterSet.CreateIterator(); It; ++It)
  1143. {
  1144. if (!OtherFilterSet.Contains(*It))
  1145. {
  1146. It.RemoveCurrent();
  1147. continue;
  1148. }
  1149. }
  1150. }
  1151. }
  1152. // Iterate over the final combined filter set to load and add to OutAssetData
  1153. for (FExtAssetData* AssetData : *CombinedFilterSetPtr)
  1154. {
  1155. OutAssetData.Add(*AssetData);
  1156. }
  1157. }
  1158. ECB_LOG(Display, TEXT("GetAssets completed in %0.4f seconds"), FPlatformTime::Seconds() - GetAssetsStartTime);
  1159. return true;
  1160. }
  1161. void FExtAssetRegistry::RunAssetsThroughFilter(TArray<FExtAssetData>& AssetDataList, const FARFilter& Filter) const
  1162. {
  1163. if (!Filter.IsEmpty())
  1164. {
  1165. TSet<FTopLevelAssetPath> RequestedClassNames;
  1166. if (Filter.bRecursiveClasses && Filter.ClassPaths.Num() > 0)
  1167. {
  1168. // First assemble a full list of requested classes from the ClassTree
  1169. // GetSubClasses includes the base classes
  1170. GetSubClasses(Filter.ClassPaths, Filter.RecursiveClassPathsExclusionSet, RequestedClassNames);
  1171. }
  1172. // todo:
  1173. #if 1
  1174. for (int32 AssetDataIdx = AssetDataList.Num() - 1; AssetDataIdx >= 0; --AssetDataIdx)
  1175. {
  1176. const FExtAssetData& AssetData = AssetDataList[AssetDataIdx];
  1177. // Package Names
  1178. if (Filter.PackageNames.Num() > 0)
  1179. {
  1180. bool bPassesPackageNames = false;
  1181. for (int32 NameIdx = 0; NameIdx < Filter.PackageNames.Num(); ++NameIdx)
  1182. {
  1183. if (Filter.PackageNames[NameIdx] == AssetData.PackageName)
  1184. {
  1185. bPassesPackageNames = true;
  1186. break;
  1187. }
  1188. }
  1189. if (!bPassesPackageNames)
  1190. {
  1191. AssetDataList.RemoveAt(AssetDataIdx);
  1192. continue;
  1193. }
  1194. }
  1195. // Package Paths
  1196. if (Filter.PackagePaths.Num() > 0)
  1197. {
  1198. bool bPassesPackagePaths = false;
  1199. if (Filter.bRecursivePaths)
  1200. {
  1201. FString AssetPackagePath = AssetData.PackageFilePath.ToString();
  1202. for (int32 PathIdx = 0; PathIdx < Filter.PackagePaths.Num(); ++PathIdx)
  1203. {
  1204. const FString Path = Filter.PackagePaths[PathIdx].ToString();
  1205. if (AssetPackagePath.StartsWith(Path))
  1206. {
  1207. // Only match the exact path or a path that starts with the target path followed by a slash
  1208. if (Path.Len() == 1 || Path.Len() == AssetPackagePath.Len() || AssetPackagePath.Mid(Path.Len(), 1) == TEXT("/"))
  1209. {
  1210. bPassesPackagePaths = true;
  1211. break;
  1212. }
  1213. }
  1214. }
  1215. }
  1216. else
  1217. {
  1218. // Non-recursive. Just request data for each requested path.
  1219. for (int32 PathIdx = 0; PathIdx < Filter.PackagePaths.Num(); ++PathIdx)
  1220. {
  1221. if (Filter.PackagePaths[PathIdx] == *AssetData.GetFolderPath())
  1222. {
  1223. bPassesPackagePaths = true;
  1224. break;
  1225. }
  1226. }
  1227. }
  1228. if (!bPassesPackagePaths)
  1229. {
  1230. AssetDataList.RemoveAt(AssetDataIdx);
  1231. continue;
  1232. }
  1233. }
  1234. // ObjectPaths
  1235. if (Filter.SoftObjectPaths.Num() > 0)
  1236. {
  1237. bool bPassesObjectPaths = Filter.SoftObjectPaths.Contains(AssetData.GetSoftObjectPath());
  1238. if (!bPassesObjectPaths)
  1239. {
  1240. AssetDataList.RemoveAt(AssetDataIdx);
  1241. continue;
  1242. }
  1243. }
  1244. // Classes
  1245. if (Filter.ClassPaths.Num() > 0)
  1246. {
  1247. bool bPassesClasses = false;
  1248. if (Filter.bRecursiveClasses)
  1249. {
  1250. // Now check against each discovered class
  1251. for (const FTopLevelAssetPath& ClassName : RequestedClassNames)
  1252. {
  1253. if (ClassName.GetAssetName() == AssetData.AssetClass)
  1254. {
  1255. bPassesClasses = true;
  1256. break;
  1257. }
  1258. }
  1259. }
  1260. else
  1261. {
  1262. // Non-recursive. Just request data for each requested classes.
  1263. for (int32 ClassIdx = 0; ClassIdx < Filter.ClassPaths.Num(); ++ClassIdx)
  1264. {
  1265. if (Filter.ClassPaths[ClassIdx].GetPackageName() == AssetData.AssetClass)
  1266. {
  1267. bPassesClasses = true;
  1268. break;
  1269. }
  1270. }
  1271. }
  1272. if (!bPassesClasses)
  1273. {
  1274. AssetDataList.RemoveAt(AssetDataIdx);
  1275. continue;
  1276. }
  1277. }
  1278. // Tags and values
  1279. if (Filter.TagsAndValues.Num() > 0)
  1280. {
  1281. bool bPassesTags = false;
  1282. for (auto FilterTagIt = Filter.TagsAndValues.CreateConstIterator(); FilterTagIt; ++FilterTagIt)
  1283. {
  1284. bool bAccept;
  1285. if (!FilterTagIt.Value().IsSet())
  1286. {
  1287. // this probably doesn't make sense, but I am preserving the original logic
  1288. bAccept = AssetData.TagsAndValues.ContainsKeyValue(FilterTagIt.Key(), FString());
  1289. }
  1290. else
  1291. {
  1292. bAccept = AssetData.TagsAndValues.ContainsKeyValue(FilterTagIt.Key(), FilterTagIt.Value().GetValue());
  1293. }
  1294. if (bAccept)
  1295. {
  1296. bPassesTags = true;
  1297. break;
  1298. }
  1299. }
  1300. if (!bPassesTags)
  1301. {
  1302. AssetDataList.RemoveAt(AssetDataIdx);
  1303. continue;
  1304. }
  1305. }
  1306. }
  1307. #endif
  1308. }
  1309. }
  1310. bool FExtAssetRegistry::GetDependencies(const FExtAssetIdentifier& AssetIdentifier, TArray<FExtAssetIdentifier>& OutDependencies, UE::AssetRegistry::EDependencyCategory InDependencyType /*= UE::AssetRegistry::EDependencyCategory::All*/, const UE::AssetRegistry::FDependencyQuery& Flags)
  1311. {
  1312. FName PackageName = AssetIdentifier.PackageName;
  1313. TArray<FName> Dependencies;
  1314. if (GetDependencies(PackageName, Dependencies, InDependencyType, Flags))
  1315. {
  1316. for (const FName& DependPackage : Dependencies)
  1317. {
  1318. OutDependencies.Add(DependPackage);
  1319. }
  1320. return true;
  1321. }
  1322. return false;
  1323. }
  1324. bool FExtAssetRegistry::GetDependencies(FName PackageName, TArray<FName>& OutDependencies, UE::AssetRegistry::EDependencyCategory InDependencyType /*= UE::AssetRegistry::EDependencyCategory::Package*/, const UE::AssetRegistry::FDependencyQuery& Flags)
  1325. {
  1326. FARFilter Filter;
  1327. Filter.PackageNames.Add(PackageName);
  1328. TArray<FExtAssetData> AssetDataList;
  1329. FExtContentBrowserSingleton::GetAssetRegistry().GetAssets(Filter, AssetDataList);
  1330. if (AssetDataList.Num() > 0)
  1331. {
  1332. FExtAssetData& AssetData = AssetDataList[0];
  1333. if (AssetData.IsValid() && AssetData.HasContentRoot())
  1334. {
  1335. TSet<FName> Dependencies;
  1336. bool bHard = !!(Flags.Required & UE::AssetRegistry::EDependencyProperty::Hard);
  1337. if (bHard)
  1338. {
  1339. Dependencies.Append(AssetData.HardDependentPackages);
  1340. }
  1341. else
  1342. {
  1343. Dependencies.Append(AssetData.SoftReferencesList);
  1344. }
  1345. TSet<FName> RemappedDependencies;
  1346. FString AssetContentDir = AssetData.AssetContentRoot.ToString();
  1347. auto& AssetContentType = AssetData.AssetContentType;
  1348. FString GameRoot(TEXT("/Game/"));
  1349. if (AssetContentType == FExtAssetData::EContentType::Plugin && !AssetContentDir.IsEmpty())
  1350. {
  1351. GameRoot = FExtAssetDataUtil::GetPluginNameFromAssetContentRoot(AssetContentDir);
  1352. }
  1353. for (const FName Depend : Dependencies)
  1354. {
  1355. FString RemappedFilePath;
  1356. if (FExtAssetDataUtil::RemapGamePackageToFullFilePath(GameRoot, Depend.ToString(), AssetContentDir, RemappedFilePath))
  1357. {
  1358. FString RemappedPackageName = FExtAssetData::ConvertFilePathToTempPackageName(RemappedFilePath);
  1359. // If haven't been cached with temp PackageName, try cache with file path
  1360. if (GetCachedAssetByPackageName(*RemappedPackageName) == nullptr)
  1361. {
  1362. GetOrCacheAssetByFilePath(*RemappedFilePath);
  1363. }
  1364. RemappedDependencies.Add(*RemappedPackageName);
  1365. }
  1366. else
  1367. {
  1368. RemappedDependencies.Add(Depend);
  1369. }
  1370. }
  1371. OutDependencies = RemappedDependencies.Array();
  1372. return true;
  1373. }
  1374. }
  1375. return false;
  1376. }
  1377. bool FExtAssetRegistry::IsGatheringAssets() const
  1378. {
  1379. return bIsGatheringAssets;
  1380. }
  1381. /////////////////////////////////////////////////////////////
  1382. // FExtAssetRegistry Impl
  1383. //
  1384. FPrimaryAssetId FExtAssetRegistry::ExtractPrimaryAssetIdFromFakeAssetData(const FExtAssetData& InAssetData)
  1385. {
  1386. static const FName PrimaryAssetFakeAssetDataPackagePath = FName("/Temp/PrimaryAsset");
  1387. if (InAssetData.PackagePath == PrimaryAssetFakeAssetDataPackagePath)
  1388. {
  1389. return FPrimaryAssetId(InAssetData.AssetClass, InAssetData.AssetName);
  1390. }
  1391. return FPrimaryAssetId();
  1392. }
  1393. void FExtAssetRegistry::ExtractAssetIdentifiersFromAssetDataList(const TArray<FExtAssetData>& AssetDataList, TArray<FExtAssetIdentifier>& OutAssetIdentifiers)
  1394. {
  1395. for (const FExtAssetData& AssetData : AssetDataList)
  1396. {
  1397. FPrimaryAssetId PrimaryAssetId = ExtractPrimaryAssetIdFromFakeAssetData(AssetData);
  1398. if (PrimaryAssetId.IsValid())
  1399. {
  1400. OutAssetIdentifiers.Add(PrimaryAssetId);
  1401. }
  1402. else
  1403. {
  1404. OutAssetIdentifiers.Add(AssetData.PackageName);
  1405. }
  1406. }
  1407. }
  1408. void FExtAssetRegistry::Shutdown()
  1409. {
  1410. StopAllBackgroundGathering();
  1411. }
  1412. namespace FContentPathUtil
  1413. {
  1414. // return: num of paths been merged
  1415. int32 CombineAndSortRootContentPaths(const TArray<FString>& InPathsToCombine, TArray<FString>& OutCombinedPaths)
  1416. {
  1417. return FPathsUtil::SortAndMergeDirs(InPathsToCombine, OutCombinedPaths);
  1418. }
  1419. void GetCleanExistPaths(const TArray<FString>& InPathsToClean, TArray<FString>& OutCleanedPaths)
  1420. {
  1421. OutCleanedPaths.Empty();
  1422. for (const FString& Path : InPathsToClean)
  1423. {
  1424. FString CleanPath = Path.TrimStartAndEnd();
  1425. if (CleanPath.IsEmpty())
  1426. {
  1427. continue;
  1428. }
  1429. CleanPath = FPaths::ConvertRelativePathToFull(Path);
  1430. FPaths::NormalizeDirectoryName(CleanPath);
  1431. if (!CleanPath.IsEmpty() && IFileManager::Get().DirectoryExists(*CleanPath))
  1432. {
  1433. OutCleanedPaths.Add(CleanPath);
  1434. }
  1435. }
  1436. }
  1437. void ExpandCachedSubsRecursively(const TMap<FName, TSet<FName>>& InCachedSubPaths, const FName& InBase, TSet<FName>& AllSubs)
  1438. {
  1439. if (InCachedSubPaths.Contains(InBase))
  1440. {
  1441. const TSet<FName>& Subs = *InCachedSubPaths.Find(InBase);
  1442. AllSubs.Append(Subs);
  1443. for (const FName& Sub : Subs)
  1444. {
  1445. ExpandCachedSubsRecursively(InCachedSubPaths, Sub, AllSubs);
  1446. }
  1447. }
  1448. }
  1449. }
  1450. void FExtAssetRegistry::LoadRootContentPaths()
  1451. {
  1452. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  1453. if (ExtContentBrowserSetting->bCacheMode)
  1454. {
  1455. RootContentPaths = State.CachedRootContentPaths;
  1456. }
  1457. else
  1458. {
  1459. // Load from config
  1460. RootContentPaths.Empty();
  1461. GConfig->GetArray(*SExtContentBrowser::SettingsIniSection, TEXT("RootContentPaths"), RootContentPaths, GEditorPerProjectIni);
  1462. }
  1463. MergeRootContentPathsWith(RootContentPaths, /*bReplaceCurrent*/ true);
  1464. }
  1465. void FExtAssetRegistry::MergeRootContentPathsWith(const TArray<FString>& InPathsToMerge, bool bReplaceCurrent)
  1466. {
  1467. TArray<FString> PathsToMerge(InPathsToMerge);
  1468. if (bReplaceCurrent)
  1469. {
  1470. RootContentPaths.Empty();
  1471. }
  1472. TSet<FString> Before(RootContentPaths);
  1473. TArray<FString> CleanedExistPaths;
  1474. FContentPathUtil::GetCleanExistPaths(PathsToMerge, CleanedExistPaths);
  1475. for (const FString& CleanPath : CleanedExistPaths)
  1476. {
  1477. RootContentPaths.Add(CleanPath);
  1478. }
  1479. CombineRootContentPaths();
  1480. SaveRootContentPaths();
  1481. CacheRootContentPathInfo();
  1482. TSet<FString> After(RootContentPaths);
  1483. TSet<FString> Removed = Before.Difference(After);
  1484. TSet<FString> Added = After.Difference(Before);
  1485. if (Removed.Num() > 0)
  1486. {
  1487. // Stop gathering
  1488. for (const FString RemovedFolder : Removed)
  1489. {
  1490. FName RemovedFolderName(*RemovedFolder);
  1491. BackgroundGatheringFolders.Remove(RemovedFolderName);
  1492. if (BackgroundFolderGatherer.IsValid())
  1493. {
  1494. BackgroundFolderGatherer->StopSearchFolder(RemovedFolder);
  1495. }
  1496. }
  1497. UpdateCacheByRemovedFolders(Removed.Array());
  1498. }
  1499. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  1500. const bool bCacheMode = ExtContentBrowserSetting->bCacheMode;
  1501. if (Added.Num() > 0 && !bCacheMode)
  1502. {
  1503. for (const FString AddedFolder : Added)
  1504. {
  1505. StartFolderGathering(AddedFolder);
  1506. }
  1507. }
  1508. if (Removed.Num() > 0 && Added.Num() == 0)
  1509. {
  1510. RootPathRemovedEvent.Broadcast(Removed.Array()[0]);
  1511. }
  1512. else if (Added.Num() > 0 && Removed.Num() == 0)
  1513. {
  1514. RootPathAddedEvent.Broadcast(Added.Array()[0]);
  1515. }
  1516. else
  1517. {
  1518. RootPathUpdatedEvent.Broadcast();
  1519. }
  1520. }
  1521. void FExtAssetRegistry::ReplaceRootContentPathsWith(const TArray<FString>& InPaths)
  1522. {
  1523. MergeRootContentPathsWith(InPaths, /*bReplaceCurrent*/ true);
  1524. }
  1525. void FExtAssetRegistry::SaveRootContentPaths()
  1526. {
  1527. State.CachedRootContentPaths = RootContentPaths;
  1528. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  1529. GConfig->SetArray(*SExtContentBrowser::SettingsIniSection, TEXT("RootContentPaths"), RootContentPaths, GEditorPerProjectIni);
  1530. GConfig->Flush(/*bRead*/ false, GEditorPerProjectIni);
  1531. }
  1532. void FExtAssetRegistry::QueryRootContentPaths(TArray<FString>& OutRootContentPaths) const
  1533. {
  1534. OutRootContentPaths = RootContentPaths;
  1535. }
  1536. bool FExtAssetRegistry::QueryRootContentPathFromFilePath(const FString& InFilePath, FString& OutRootContentPath)
  1537. {
  1538. for (const auto& Root : RootContentPaths)
  1539. {
  1540. if (FPathsUtil::IsSubOrSamePath(InFilePath, Root))
  1541. {
  1542. OutRootContentPath = Root;
  1543. return true;
  1544. }
  1545. }
  1546. return false;
  1547. }
  1548. bool FExtAssetRegistry::QueryRootContentPathInfo(const FString& InRootContentPath, FText* OutDisplayName, FExtAssetData::EContentType* OutContentType, FString* OutAssetContentRoot) const
  1549. {
  1550. if (const auto& PathInfoPtr = RootContentPathsInfo.Find(InRootContentPath))
  1551. {
  1552. if (OutContentType)
  1553. {
  1554. *OutContentType = PathInfoPtr->ContentType;
  1555. }
  1556. if (OutDisplayName)
  1557. {
  1558. *OutDisplayName = PathInfoPtr->DisplayName;
  1559. }
  1560. if (OutAssetContentRoot)
  1561. {
  1562. *OutAssetContentRoot = PathInfoPtr->AssetContentRoot;
  1563. }
  1564. return true;
  1565. }
  1566. return false;
  1567. }
  1568. bool FExtAssetRegistry::ParseAssetContentRoot(const FString& InFilePath, FString& OutRelativePath, FString& OutAssetContentRoot, FExtAssetData::EContentType& OutAssetContentType, TSet<FName>* InAllDependencies)
  1569. {
  1570. bool bFoundRootPath = false;
  1571. FString AssetContentRoot;
  1572. FString RelativePath;
  1573. if (GetAssetContentRoot(InFilePath, AssetContentRoot))
  1574. {
  1575. RelativePath = InFilePath.Mid(FCString::Strlen(*AssetContentRoot));
  1576. FName AssetContentRootName(*AssetContentRoot);
  1577. if (State.CachedAssetContentType.Contains(AssetContentRootName))
  1578. {
  1579. OutAssetContentType = State.CachedAssetContentType[AssetContentRootName];
  1580. }
  1581. bFoundRootPath = true;
  1582. }
  1583. else
  1584. {
  1585. FString AssetContentRootHost;
  1586. #if ECB_FEA_IMPORT_PLUGIN
  1587. // Plugin Content
  1588. if (!bFoundRootPath)
  1589. {
  1590. bFoundRootPath = FExtContentDirFinder::FindWithFile(/*AssetFilePath=*/ InFilePath, /*FileToFind=*/ TEXT(".uplugin"), /*bExtension=*/ true, /*FolderPattern=*/ TEXT("/Content"), /*FoundRoot=*/ AssetContentRoot, /*RelativePath=*/ RelativePath);
  1591. if (bFoundRootPath)
  1592. {
  1593. AssetContentRootHost = AssetContentRoot / TEXT("..");
  1594. OutAssetContentType = FExtAssetData::EContentType::Plugin;
  1595. }
  1596. }
  1597. #endif
  1598. // Project Content
  1599. if (!bFoundRootPath)
  1600. {
  1601. bFoundRootPath = FExtContentDirFinder::FindWithFile(InFilePath, TEXT(".uproject"), /*bExtension*/ true, TEXT("/Content"), AssetContentRoot, RelativePath);
  1602. if (bFoundRootPath)
  1603. {
  1604. AssetContentRootHost = AssetContentRoot / TEXT("..");
  1605. OutAssetContentType = FExtAssetData::EContentType::Project;
  1606. }
  1607. }
  1608. // Vault Cache Content
  1609. if (!bFoundRootPath)
  1610. {
  1611. bFoundRootPath = FExtContentDirFinder::FindWithFile(InFilePath, TEXT("manifest"), /*bExtension*/ false, TEXT("/data/Content"), AssetContentRoot, RelativePath);
  1612. if (bFoundRootPath)
  1613. {
  1614. AssetContentRootHost = AssetContentRoot / TEXT("../..");
  1615. OutAssetContentType = FExtAssetData::EContentType::VaultCache;
  1616. }
  1617. }
  1618. // Project Content without .uproject file
  1619. if (!bFoundRootPath)
  1620. {
  1621. bFoundRootPath = FExtContentDirFinder::FindFolder(InFilePath, TEXT("Content"), AssetContentRoot, RelativePath);
  1622. if (bFoundRootPath)
  1623. {
  1624. AssetContentRootHost = AssetContentRoot / TEXT("..");
  1625. OutAssetContentType = FExtAssetData::EContentType::PartialProject;
  1626. }
  1627. }
  1628. #if ECB_WIP_IMPORT_ORPHAN && 1
  1629. if (!bFoundRootPath)
  1630. {
  1631. // todo: try getting asset content root from dependencies
  1632. FString RootContentPath;
  1633. if (QueryRootContentPathFromFilePath(InFilePath, RootContentPath) && InAllDependencies)
  1634. {
  1635. // Get all paths
  1636. TSet<FName> SubPaths;
  1637. SubPaths.Add(*RootContentPath);
  1638. FExtContentBrowserSingleton::GetAssetRegistry().GetCachedSubPaths(*RootContentPath, SubPaths, /*bRecursively = */ true);
  1639. // also include parent folder in case user select one step deeper
  1640. FString Parent = RootContentPath / TEXT("..");
  1641. FPaths::CollapseRelativeDirectories(Parent);
  1642. SubPaths.Add(*Parent);
  1643. for (const FName& Dir : SubPaths)
  1644. {
  1645. AssetContentRoot = Dir.ToString();
  1646. RelativePath = InFilePath.RightChop(AssetContentRoot.Len());
  1647. if (InAllDependencies->Num() > 0)
  1648. {
  1649. for (const FName& DependPackageName : *InAllDependencies)
  1650. {
  1651. FString DependPackageString = DependPackageName.ToString();
  1652. if (DependPackageString.StartsWith("/Script/"))
  1653. {
  1654. continue;
  1655. }
  1656. FString PackageRoot = FExtAssetDataUtil::GetPackageRootFromFullPackagePath(DependPackageString);
  1657. FString DependentFilePath;
  1658. if (FExtAssetDataUtil::RemapGamePackageToFullFilePath(PackageRoot, DependPackageString, AssetContentRoot, DependentFilePath))
  1659. {
  1660. ECB_LOG(Display, TEXT("[ParseAssetContent] For Orphan Aset: Found dependency: %s In %s"), *DependentFilePath, *AssetContentRoot);
  1661. bFoundRootPath = true;
  1662. break;
  1663. }
  1664. }
  1665. }
  1666. if (bFoundRootPath)
  1667. break;
  1668. }
  1669. if (bFoundRootPath)
  1670. {
  1671. AssetContentRootHost = AssetContentRoot;
  1672. OutAssetContentType = FExtAssetData::EContentType::Orphan;
  1673. }
  1674. }
  1675. }
  1676. #endif
  1677. if (bFoundRootPath)
  1678. {
  1679. // Cache Asset Content Root
  1680. FString ContentRootConfigDir = FExtConfigUtil::GetConfigDirByContentRoot(AssetContentRoot);
  1681. FPaths::CollapseRelativeDirectories(AssetContentRootHost);
  1682. AddAssetContentRoot(AssetContentRoot, AssetContentRootHost, ContentRootConfigDir, OutAssetContentType);
  1683. }
  1684. }
  1685. if (bFoundRootPath)
  1686. {
  1687. FPaths::NormalizeFilename(RelativePath);
  1688. FPaths::NormalizeDirectoryName(AssetContentRoot);
  1689. if (RelativePath.StartsWith(TEXT("/")))
  1690. {
  1691. RelativePath = RelativePath.Mid(1);
  1692. }
  1693. OutRelativePath = RelativePath;
  1694. OutAssetContentRoot = AssetContentRoot;
  1695. return true;
  1696. }
  1697. return false;
  1698. }
  1699. void FExtAssetRegistry::AddAssetContentRoot(const FString& InAssetContentRoot, const FString& InAssetContentRootHost, const FString& InConfigDir, FExtAssetData::EContentType InContentType)
  1700. {
  1701. int32 NumCached = State.CachedAssetContentRoots.Num();
  1702. bool bAdded = false;
  1703. FName AssetContentRootName(*InAssetContentRoot);
  1704. for (int32 Index = 0; Index < NumCached; ++Index)
  1705. {
  1706. FString RootDir = State.CachedAssetContentRoots[Index].ToString();
  1707. if (!FPaths::IsSamePath(InAssetContentRoot, RootDir) && InAssetContentRoot.Len() >= RootDir.Len())
  1708. {
  1709. State.CachedAssetContentRoots.Insert(AssetContentRootName, Index);
  1710. bAdded = true;
  1711. break;
  1712. }
  1713. }
  1714. if (!bAdded)
  1715. {
  1716. State.CachedAssetContentRoots.Add(AssetContentRootName);
  1717. }
  1718. State.CachedAssetContentType.FindOrAdd(AssetContentRootName) = InContentType;
  1719. State.CachedAssetContentRootConfigDirs.FindOrAdd(AssetContentRootName) = *InConfigDir;
  1720. if (!InAssetContentRootHost.IsEmpty())
  1721. {
  1722. State.CachedAssetContentRootHosts.FindOrAdd(*InAssetContentRootHost) = AssetContentRootName;
  1723. CacheFolderColor(AssetContentRootName);
  1724. }
  1725. }
  1726. bool FExtAssetRegistry::GetAssetContentRoot(const FString& InFileOrFolderPath, FString& OutFoundAssetContentDir) const
  1727. {
  1728. for (int32 Index = 0; Index < State.CachedAssetContentRoots.Num(); ++Index)
  1729. {
  1730. FString RootDir = State.CachedAssetContentRoots[Index].ToString();
  1731. //if (InFilePath.StartsWith(RootDir))
  1732. if (FPathsUtil::IsFileInDir(InFileOrFolderPath, RootDir))
  1733. {
  1734. OutFoundAssetContentDir = RootDir;
  1735. return true;
  1736. }
  1737. }
  1738. return false;
  1739. }
  1740. FExtAssetData::EContentType FExtAssetRegistry::GetAssetContentRootContentType(const FString& InAssetContentRoot) const
  1741. {
  1742. TMap<FName, FExtAssetData::EContentType> CachedAssetContentType;
  1743. FName AssetContentRootName = *InAssetContentRoot;
  1744. if (const FExtAssetData::EContentType* ContentType = State.CachedAssetContentType.Find(AssetContentRootName))
  1745. {
  1746. return *ContentType;
  1747. }
  1748. return FExtAssetData::EContentType::Unknown;
  1749. }
  1750. bool FExtAssetRegistry::GetFolderColor(const FString& InFolderPath, FLinearColor& OutFolderColor) const
  1751. {
  1752. if (const FLinearColor* FoundColor = State.CachedFolderColors.Find(*InFolderPath))
  1753. {
  1754. OutFolderColor = *FoundColor;
  1755. return true;
  1756. }
  1757. return false;
  1758. }
  1759. bool FExtAssetRegistry::IsAsetContentRootHasFolderColor(const FString& InAssetContentRoot) const
  1760. {
  1761. return State.CachedFolderColorIndices.Contains(*InAssetContentRoot);
  1762. }
  1763. void FExtAssetRegistry::GetAssetContentRootColoredFolders(const FString& InAssetContentRoot, TArray<FName>& OutColoredFolders) const
  1764. {
  1765. if (auto* Folders = State.CachedFolderColorIndices.Find(*InAssetContentRoot))
  1766. {
  1767. OutColoredFolders = *Folders;
  1768. }
  1769. }
  1770. void FExtAssetRegistry::GetAssetContentRootFolderColors(const FString& InAssetContentRoot, TMap<FName, FLinearColor>& OutFoldersColor) const
  1771. {
  1772. OutFoldersColor.Empty();
  1773. if (const TArray<FName>* Folders = State.CachedFolderColorIndices.Find(*InAssetContentRoot))
  1774. {
  1775. for (const auto& Folder : *Folders)
  1776. {
  1777. FLinearColor FolderColor;
  1778. if (GetFolderColor(Folder.ToString(), FolderColor))
  1779. {
  1780. OutFoldersColor.Add(Folder, FolderColor);
  1781. }
  1782. }
  1783. }
  1784. }
  1785. bool FExtAssetRegistry::IsRootFolder(const FString& InPath) const
  1786. {
  1787. for (const FString& RootPath : RootContentPaths)
  1788. {
  1789. //if (InPath.Equals(RootPath, ESearchCase::CaseSensitive))
  1790. if (FPaths::IsSamePath(RootPath, InPath))
  1791. {
  1792. return true;
  1793. }
  1794. }
  1795. return false;
  1796. }
  1797. bool FExtAssetRegistry::IsRootFolders(const TArray<FString>& InPaths) const
  1798. {
  1799. bool bIsRootFolders = false;
  1800. if (InPaths.Num() > 0)
  1801. {
  1802. bIsRootFolders = true;
  1803. for (const FString& Path : InPaths)
  1804. {
  1805. if (!IsRootFolder(Path))
  1806. {
  1807. bIsRootFolders = false;
  1808. break;
  1809. }
  1810. }
  1811. }
  1812. return bIsRootFolders;
  1813. }
  1814. bool FExtAssetRegistry::AddRootFolder(const FString& InPath, TArray<FString>* OutAdded, TArray<FString>* OutCombined)
  1815. {
  1816. if (!InPath.IsEmpty())
  1817. {
  1818. FString FolderPath = FPaths::ConvertRelativePathToFull(InPath);
  1819. FPaths::NormalizeDirectoryName(FolderPath);
  1820. // Validate
  1821. if (FolderPath.IsEmpty())
  1822. {
  1823. return false;
  1824. }
  1825. // Exist or sub dir of existing?
  1826. for (const FString& RootPath : RootContentPaths)
  1827. {
  1828. const bool bExistOrSubPath = FPathsUtil::IsSubOrSamePath(FolderPath, RootPath);
  1829. if (bExistOrSubPath)
  1830. {
  1831. if (OutCombined)
  1832. {
  1833. *OutCombined = {FolderPath};
  1834. }
  1835. return true;
  1836. }
  1837. }
  1838. TSet<FString> Before(RootContentPaths);
  1839. {
  1840. RootContentPaths.Add(FolderPath);
  1841. CombineRootContentPaths();
  1842. SaveRootContentPaths();
  1843. }
  1844. TSet<FString> After(RootContentPaths);
  1845. // Old paths been consolidated, clear cache to trigger re-cache
  1846. TSet<FString> Combined = Before.Difference(After);
  1847. TSet<FString> Added = After.Difference(Before);
  1848. if (Combined.Num() > 0)
  1849. {
  1850. // Only if Removed is not sub path of Added
  1851. TArray<FString> RemovedForUpdateCache;
  1852. for (const FString& RemovedFolder : Combined)
  1853. {
  1854. bool bIsSubPath = false;
  1855. for (const FString& AddedFolder : Added)
  1856. {
  1857. if (FPathsUtil::IsSubOrSamePath(RemovedFolder, AddedFolder))
  1858. {
  1859. bIsSubPath = true;
  1860. break;
  1861. }
  1862. }
  1863. if (!bIsSubPath)
  1864. {
  1865. RemovedForUpdateCache.Add(RemovedFolder);
  1866. }
  1867. }
  1868. UpdateCacheByRemovedFolders(RemovedForUpdateCache);
  1869. if (OutCombined)
  1870. {
  1871. *OutCombined = Combined.Array();
  1872. }
  1873. }
  1874. CacheRootContentPathInfo();
  1875. if (Added.Num() > 0)
  1876. {
  1877. #if ECB_FEA_ASYNC_FOLDER_DISCOVERY
  1878. StartFolderGathering(Added.Array()[0]); // todo: gather multiple folders
  1879. #endif
  1880. RootPathAddedEvent.Broadcast(Added.Array()[0]);
  1881. if (OutAdded)
  1882. {
  1883. *OutAdded = Added.Array();
  1884. }
  1885. }
  1886. return true;
  1887. }
  1888. return false;
  1889. }
  1890. bool FExtAssetRegistry::ReloadRootFolders(const TArray<FString>& InPaths, TArray<FString>* OutReloaded /*= nullptr*/)
  1891. {
  1892. TArray<FString> Removed;
  1893. if (RemoveRootFolders(InPaths, &Removed))
  1894. {
  1895. for (const FString& ReloadFolder : Removed)
  1896. {
  1897. AddRootFolder(ReloadFolder);
  1898. if (OutReloaded)
  1899. {
  1900. OutReloaded->Add(ReloadFolder);
  1901. }
  1902. }
  1903. return true;
  1904. }
  1905. return false;
  1906. }
  1907. bool FExtAssetRegistry::RemoveRootFolders(const TArray<FString>& InPaths, TArray<FString>* OutRemovd)
  1908. {
  1909. TSet<FString> Before(RootContentPaths);
  1910. {
  1911. for (const FString& InPath : InPaths)
  1912. {
  1913. if (!InPath.IsEmpty())
  1914. {
  1915. FString FolderPath = FPaths::ConvertRelativePathToFull(InPath);
  1916. FPaths::NormalizeDirectoryName(FolderPath);
  1917. RootContentPaths.Remove(FolderPath);
  1918. }
  1919. }
  1920. }
  1921. TSet<FString> After(RootContentPaths);
  1922. TSet<FString> Removed = Before.Difference(After);
  1923. if (Removed.Num() > 0)
  1924. {
  1925. CombineRootContentPaths();
  1926. SaveRootContentPaths();
  1927. // Stop gathering
  1928. for (const FString RemovedFolder : Removed)
  1929. {
  1930. FName RemovedFolderName(*RemovedFolder);
  1931. BackgroundGatheringFolders.Remove(RemovedFolderName);
  1932. if (BackgroundFolderGatherer.IsValid())
  1933. {
  1934. BackgroundFolderGatherer->StopSearchFolder(RemovedFolder);
  1935. }
  1936. }
  1937. UpdateCacheByRemovedFolders(Removed.Array());
  1938. if (OutRemovd)
  1939. {
  1940. *OutRemovd = Removed.Array();
  1941. }
  1942. CacheRootContentPathInfo();
  1943. // todo: should RootPathRemovedEvent take all removed paths?
  1944. RootPathRemovedEvent.Broadcast(InPaths[0]);
  1945. return true;
  1946. }
  1947. return false;
  1948. }
  1949. void FExtAssetRegistry::ReGatheringFolders(const TArray<FString>& InPaths)
  1950. {
  1951. UpdateCacheByRemovedFolders(InPaths);
  1952. for (const FString& InPath : InPaths)
  1953. {
  1954. StartFolderGathering(InPath);
  1955. }
  1956. FolderStartGatheringEvent.Broadcast(InPaths);
  1957. }
  1958. void FExtAssetRegistry::CacheAssets(const struct FARFilter& InFilter)
  1959. {
  1960. double GetAssetsStartTime = FPlatformTime::Seconds();
  1961. double ParseAssetsStartTime = FPlatformTime::Seconds();
  1962. double PrepareAssetsTime = 0.;
  1963. if (!IsFilterValid(InFilter, /*bAllowRecursion = */ true) || InFilter.IsEmpty())
  1964. {
  1965. return;
  1966. }
  1967. TSet<FExtAssetData*> FilteredAssets;
  1968. {
  1969. // Expand recursion on filter
  1970. FARFilter Filter;
  1971. ExpandRecursiveFilter(InFilter, Filter);
  1972. // Verify filter input. If all assets are needed, use GetAllAssets() instead.
  1973. if (!IsFilterValid(Filter, /*bAllowRecursion = */ false) || InFilter.IsEmpty())
  1974. {
  1975. return;
  1976. }
  1977. // Prepare a set of each filter component for fast searching
  1978. TSet<FName> FilterPackagePaths(Filter.PackagePaths);
  1979. TSet<FTopLevelAssetPath> FilterClassNames(Filter.ClassPaths);
  1980. TSet<FName> FilterPackageNames(Filter.PackageNames);
  1981. int32 TotalTasks = 0;
  1982. if (FilterPackagePaths.Num() > 0)
  1983. {
  1984. ++TotalTasks;
  1985. }
  1986. //if (FilterClassNames.Num()) ++TotalTasks;
  1987. if (FilterPackageNames.Num() > 0)
  1988. {
  1989. ++TotalTasks;
  1990. }
  1991. ECB_LOG(Display, TEXT("CacheAssets start...%d PackagePaths; %d ClassNames; %d PackageNames => %d tasks)"), FilterPackagePaths.Num(), FilterClassNames.Num(), FilterPackageNames.Num(), TotalTasks);
  1992. const int32 MinPreparePathsToShowDialog = 50;
  1993. const int32 TotalPathsToPrepare = FilterPackagePaths.Num() + FilterClassNames.Num() + FilterPackageNames.Num();
  1994. const bool bShowPrepareDialog = TotalPathsToPrepare >= MinPreparePathsToShowDialog;
  1995. FScopedSlowTask PrepareTask(TotalTasks, LOCTEXT("ScanningAssets_Prepare", "Building Asset List..."));
  1996. if (bShowPrepareDialog)
  1997. {
  1998. PrepareTask.MakeDialog(/*bShowCancelDialog =*/ true);
  1999. }
  2000. FText ProgressFormat = LOCTEXT("ScanningAssets_Prepare", "Building Asset List...{0}");
  2001. bool bPrepareTaskCanceled = false;
  2002. const bool bDealyParse = true;
  2003. // On disk package paths
  2004. //PrepareTask.EnterProgressFrame(1.f);
  2005. if (!bPrepareTaskCanceled && FilterPackagePaths.Num())
  2006. {
  2007. float Progress = 1.f / FilterPackagePaths.Num();
  2008. for (FName PackagePath : FilterPackagePaths)
  2009. {
  2010. if (bShowPrepareDialog)
  2011. {
  2012. PrepareTask.EnterProgressFrame(Progress, FText::Format(ProgressFormat, FText::FromString(FPaths::GetBaseFilename(PackagePath.ToString()))));
  2013. if (PrepareTask.ShouldCancel())
  2014. {
  2015. bPrepareTaskCanceled = true;
  2016. break;
  2017. }
  2018. }
  2019. if (const TArray<FExtAssetData*>* PathAssets = GetOrCacheAssetsByFolder(PackagePath, bDealyParse).Find(PackagePath))
  2020. {
  2021. FilteredAssets.Append(*PathAssets);
  2022. }
  2023. }
  2024. }
  2025. // On disk classes
  2026. //PrepareTask.EnterProgressFrame(1.f);
  2027. if (!bPrepareTaskCanceled && FilterClassNames.Num())
  2028. {
  2029. #if ECB_TODO // ALready covered in expanded filters step?
  2030. float Progress = 1.f / FilterPackagePaths.Num();
  2031. for (const FName& PackagePath : FilterPackagePaths)
  2032. {
  2033. PrepareTask.EnterProgressFrame(Progress);
  2034. if (PrepareTask.ShouldCancel())
  2035. {
  2036. bPrepareTaskCanceled = true;
  2037. break;
  2038. }
  2039. TSet<FName> SubPaths;
  2040. GetOrCacheSubPaths(PackagePath, SubPaths, /*bInRecurse =*/ true);
  2041. for (const FName& SubPath : SubPaths)
  2042. {
  2043. if (PrepareTask.ShouldCancel())
  2044. {
  2045. bPrepareTaskCanceled = true;
  2046. break;
  2047. }
  2048. if (PrepareTask.ShouldCancel())
  2049. {
  2050. bPrepareTaskCanceled = true;
  2051. break;
  2052. }
  2053. if (const TArray<FExtAssetData*>* SubPathAssets = GetOrCacheAssetsByFolder(SubPath, bDealyParse).Find(SubPath))
  2054. {
  2055. FilteredAssets.Append(*SubPathAssets);
  2056. }
  2057. }
  2058. }
  2059. #endif
  2060. }
  2061. // On disk package names
  2062. //PrepareTask.EnterProgressFrame(1.f);
  2063. if (!bPrepareTaskCanceled && FilterPackageNames.Num())
  2064. {
  2065. float Progress = 1.f / FilterPackageNames.Num();
  2066. for (FName PackageName : FilterPackageNames)
  2067. {
  2068. if (bShowPrepareDialog)
  2069. {
  2070. PrepareTask.EnterProgressFrame(Progress, FText::Format(ProgressFormat, FText::FromString(FPaths::GetBaseFilename(PackageName.ToString()))));
  2071. if (PrepareTask.ShouldCancel())
  2072. {
  2073. bPrepareTaskCanceled = true;
  2074. break;
  2075. }
  2076. }
  2077. const FName* FilePath = State.CachedPackageNameToFilePathMap.Find(PackageName);
  2078. if (FilePath != nullptr)
  2079. {
  2080. if (FExtAssetData* AssetData = GetOrCacheAssetByFilePath(*FilePath, bDealyParse))
  2081. {
  2082. FilteredAssets.Add(AssetData);
  2083. }
  2084. }
  2085. }
  2086. }
  2087. if (bPrepareTaskCanceled)
  2088. {
  2089. return;
  2090. }
  2091. }
  2092. // Parsing
  2093. TSet<FExtAssetData*> ToParseAssets;
  2094. for (FExtAssetData* AssetData : FilteredAssets)
  2095. {
  2096. if (!AssetData->IsParsed())
  2097. {
  2098. ToParseAssets.Add(AssetData);
  2099. }
  2100. }
  2101. PrepareAssetsTime = FPlatformTime::Seconds() - ParseAssetsStartTime;
  2102. const int32 NumToParseAssets = ToParseAssets.Num();
  2103. if (NumToParseAssets > 0)
  2104. {
  2105. const int32 MinAssetToShowDialog = 50;
  2106. const bool bShowProgress = NumToParseAssets >= MinAssetToShowDialog;
  2107. // Restore keyboard focus steal by SlowTask dialog
  2108. TSharedPtr<SWidget> PreviousFocusedWidget = FSlateApplication::Get().GetKeyboardFocusedWidget();
  2109. {
  2110. FScopedSlowTask SlowTask(NumToParseAssets, FText::Format(LOCTEXT("ScanningAssets_Scan", "Scanning {0} Assets..."), FText::AsNumber(NumToParseAssets)));
  2111. FText ScanProgressFormat = LOCTEXT("ScanningAssets_ScanFormatText", "Scanning {0} Assets...{1}");
  2112. if (bShowProgress)
  2113. {
  2114. SlowTask.MakeDialog(/*bShowCancelDialog =*/ true);
  2115. }
  2116. for (FExtAssetData* AssetData : ToParseAssets)
  2117. {
  2118. AssetData->Parse();
  2119. // Update cache for class and asset tags
  2120. State.CacheNewAsset(AssetData);
  2121. if (bShowProgress && SlowTask.ShouldCancel())
  2122. {
  2123. break;
  2124. }
  2125. if (bShowProgress)
  2126. {
  2127. SlowTask.EnterProgressFrame(1.f, FText::Format(ScanProgressFormat, FText::AsNumber(NumToParseAssets), FText::FromString(FPaths::GetBaseFilename(AssetData->PackageFilePath.ToString()))));
  2128. }
  2129. }
  2130. }
  2131. if (bShowProgress && PreviousFocusedWidget.IsValid())
  2132. {
  2133. FSlateApplication::Get().SetKeyboardFocus(PreviousFocusedWidget, EFocusCause::SetDirectly);
  2134. }
  2135. }
  2136. ECB_LOG(Display, TEXT("CacheAssets completed in %0.4f seconds. (Prepare in %0.4f secons)"), FPlatformTime::Seconds() - GetAssetsStartTime, PrepareAssetsTime);
  2137. }
  2138. void FExtAssetRegistry::CacheAssetsAsync(const struct FARFilter& InFilter)
  2139. {
  2140. double GetAssetsStartTime = FPlatformTime::Seconds();
  2141. double ParseAssetsStartTime = FPlatformTime::Seconds();
  2142. double PrepareAssetsTime = 0.;
  2143. if (!IsFilterValid(InFilter, /*bAllowRecursion = */ true) || InFilter.IsEmpty())
  2144. {
  2145. return;
  2146. }
  2147. TSet<FName> FilteredAssetPaths;
  2148. {
  2149. // Expand recursion on filter
  2150. FARFilter Filter;
  2151. ExpandRecursiveFilter(InFilter, Filter);
  2152. // Verify filter input. If all assets are needed, use GetAllAssets() instead.
  2153. if (!IsFilterValid(Filter, /*bAllowRecursion = */ false) || InFilter.IsEmpty())
  2154. {
  2155. return;
  2156. }
  2157. // Prepare a set of each filter component for fast searching
  2158. TSet<FName> FilterPackagePaths(Filter.PackagePaths);
  2159. TSet<FTopLevelAssetPath> FilterClassNames(Filter.ClassPaths);
  2160. TSet<FName> FilterPackageNames(Filter.PackageNames);
  2161. ECB_LOG(Display, TEXT("CacheAssets start...%d PackagePaths; %d ClassNames; %d PackageNames)"), FilterPackagePaths.Num(), FilterClassNames.Num(), FilterPackageNames.Num());
  2162. // On disk package paths
  2163. if (FilterPackagePaths.Num())
  2164. {
  2165. const auto& CachedFilePathsByFolder = State.CachedFilePathsByFolder;
  2166. for (FName PackagePath : FilterPackagePaths)
  2167. {
  2168. if (const TArray<FName>* FoundAssetPaths = CachedFilePathsByFolder.Find(PackagePath))
  2169. {
  2170. FilteredAssetPaths.Append(*FoundAssetPaths);
  2171. }
  2172. }
  2173. }
  2174. // On disk classes
  2175. //PrepareTask.EnterProgressFrame(1.f);
  2176. if (FilterClassNames.Num())
  2177. {
  2178. #if ECB_TODO // ALready covered in expanded filters step?
  2179. for (const FName& ClassName : FilterClassNames)
  2180. {
  2181. }
  2182. #endif
  2183. }
  2184. // On disk package names
  2185. if (FilterPackageNames.Num())
  2186. {
  2187. for (FName PackageName : FilterPackageNames)
  2188. {
  2189. const FName* FilePath = State.CachedPackageNameToFilePathMap.Find(PackageName);
  2190. if (FilePath != nullptr)
  2191. {
  2192. FilteredAssetPaths.Add(*FilePath);
  2193. }
  2194. }
  2195. }
  2196. }
  2197. // To Parsing
  2198. TSet<FName> ToParseAssets;
  2199. if (FilteredAssetPaths.Num() > 0)
  2200. {
  2201. for (const FName& AssetPathName : FilteredAssetPaths)
  2202. {
  2203. if (FExtAssetData* AssetData = GetCachedAssetByFilePath(AssetPathName))
  2204. {
  2205. if (AssetData->IsParsed())
  2206. {
  2207. continue;
  2208. }
  2209. }
  2210. ToParseAssets.Add(AssetPathName);
  2211. }
  2212. }
  2213. StartOrCancelAssetGathering(ToParseAssets.Array(), FilteredAssetPaths.Num());
  2214. ECB_LOG(Display, TEXT("CacheAssetsAsync completed in %0.4f seconds. Kick off %d assets to gathering. (Prepare in %0.4f secons)"), FPlatformTime::Seconds() - GetAssetsStartTime, ToParseAssets.Num());
  2215. }
  2216. void FExtAssetRegistry::GetOrCacheSubPaths(const FName& InBasePath, TSet<FName>& OutSubPaths, bool bInRecurse)
  2217. {
  2218. auto& CachedSubPaths = State.CachedSubPaths;
  2219. if (!bInRecurse && CachedSubPaths.Contains(InBasePath))
  2220. {
  2221. OutSubPaths.Append(*CachedSubPaths.Find(InBasePath));
  2222. return;
  2223. }
  2224. if (bInRecurse)
  2225. {
  2226. TSet<FName> SubPaths;
  2227. FContentPathUtil::ExpandCachedSubsRecursively(CachedSubPaths, InBasePath, SubPaths);
  2228. for (const FName& SubPath : SubPaths)
  2229. {
  2230. OutSubPaths.Add(SubPath);
  2231. }
  2232. //FExtPackageUtils::GetDirectoriesRecursively(InBasePath.ToString(), SubPaths);
  2233. }
  2234. else
  2235. {
  2236. TArray<FString> SubPaths;
  2237. FExtPackageUtils::GetDirectories(InBasePath.ToString(), SubPaths);
  2238. for (const FString& SubPath : SubPaths)
  2239. {
  2240. OutSubPaths.Add(*SubPath);
  2241. }
  2242. }
  2243. // Cancel background folder gathering result?
  2244. if (bInRecurse && BackgroundGatheringFolders.Contains(InBasePath))
  2245. {
  2246. BackgroundGatheringFolders.Remove(InBasePath);
  2247. }
  2248. }
  2249. void FExtAssetRegistry::GetCachedSubPaths(const FName& InBasePath, TSet<FName>& OutPathList, bool bInRecurse)
  2250. {
  2251. auto& CachedSubPaths = State.CachedSubPaths;
  2252. if (bInRecurse)
  2253. {
  2254. if (IsFolderBackgroundGathering(InBasePath.ToString()))
  2255. {
  2256. FString BasePath = InBasePath.ToString();
  2257. for (auto It = CachedSubPaths.CreateIterator(); It; ++It)
  2258. {
  2259. const FName ParentPath = It->Key;
  2260. if (FPathsUtil::IsSubOrSamePath(ParentPath.ToString(), BasePath))
  2261. {
  2262. OutPathList.Append(It->Value);
  2263. }
  2264. }
  2265. }
  2266. else if (CachedSubPaths.Contains(InBasePath))
  2267. {
  2268. FContentPathUtil::ExpandCachedSubsRecursively(CachedSubPaths, InBasePath, OutPathList);
  2269. //OutPathList.Append(*CachedSubPaths.Find(InBasePath));
  2270. }
  2271. }
  2272. if (!bInRecurse && CachedSubPaths.Contains(InBasePath))
  2273. {
  2274. OutPathList.Append(*CachedSubPaths.Find(InBasePath));
  2275. }
  2276. }
  2277. FExtAssetDependencyInfo FExtAssetRegistry::GetOrCacheAssetDependencyInfo(const FExtAssetData& InAssetData, bool bShowProgess)
  2278. {
  2279. const FName PackageFilePath = InAssetData.PackageFilePath;
  2280. auto& CachedDependencyInfoByFilePath = State.CachedDependencyInfoByFilePath;
  2281. if (FExtAssetDependencyInfo* FoundDependencyInfo = CachedDependencyInfoByFilePath.Find(PackageFilePath))
  2282. {
  2283. return *FoundDependencyInfo;
  2284. }
  2285. FExtAssetDependencyInfo DependencyInfo;
  2286. if (!InAssetData.IsUAsset())
  2287. {
  2288. if (InAssetData.PackageName != NAME_None)
  2289. {
  2290. DependencyInfo.AssetStatus = FExtPackageUtils::DoesPackageExist(InAssetData.PackageName.ToString()) ? EDependencyNodeStatus::Valid : EDependencyNodeStatus::Missing;
  2291. }
  2292. else
  2293. {
  2294. DependencyInfo.AssetStatus = EDependencyNodeStatus::Invalid;
  2295. }
  2296. }
  2297. else if (!InAssetData.IsValid())
  2298. {
  2299. DependencyInfo.AssetStatus = EDependencyNodeStatus::Invalid;
  2300. }
  2301. else if (!InAssetData.HasContentRoot())
  2302. {
  2303. DependencyInfo.AssetStatus = EDependencyNodeStatus::Unknown;
  2304. }
  2305. else
  2306. {
  2307. FString AssetContentDir = InAssetData.AssetContentRoot.ToString();
  2308. auto& AssetContentType = InAssetData.AssetContentType;
  2309. DependencyInfo.AssetStatus = FExtAssetDependencyWalker::GatherDependencies(InAssetData, AssetContentDir, AssetContentType, DependencyInfo, bShowProgess);
  2310. }
  2311. if (PackageFilePath != NAME_None && DependencyInfo.AssetStatus != EDependencyNodeStatus::AbortGathering)
  2312. {
  2313. State.CachedDependencyInfoByFilePath.Add(PackageFilePath, DependencyInfo);
  2314. }
  2315. return DependencyInfo;
  2316. }
  2317. const FExtAssetDependencyInfo* FExtAssetRegistry::GetCachedAssetDependencyInfo(const FExtAssetData& InAssetData) const
  2318. {
  2319. const FName PackageFilePath = InAssetData.PackageFilePath;
  2320. auto& CachedDependencyInfoByFilePath = State.CachedDependencyInfoByFilePath;
  2321. if (const FExtAssetDependencyInfo* FoundDependencyInfo = CachedDependencyInfoByFilePath.Find(PackageFilePath))
  2322. {
  2323. return FoundDependencyInfo;
  2324. }
  2325. return nullptr;
  2326. }
  2327. bool FExtAssetRegistry::RemoveCachedAssetDependencyInfo(const FExtAssetData& InAssetData)
  2328. {
  2329. auto& CachedDependencyInfoByFilePath = State.CachedDependencyInfoByFilePath;
  2330. const FName PackageFilePath = InAssetData.PackageFilePath;
  2331. int32 NumRemoved = CachedDependencyInfoByFilePath.Remove(PackageFilePath);
  2332. return NumRemoved > 0;
  2333. }
  2334. bool FExtAssetRegistry::IsCachedDependencyInfoInValid(const FExtAssetData& InAssetData, bool bTreatSoftErrorAsInvalid /*= false*/) const
  2335. {
  2336. if (const FExtAssetDependencyInfo* CachedDependencyInfo = GetCachedAssetDependencyInfo(InAssetData))
  2337. {
  2338. if (bTreatSoftErrorAsInvalid)
  2339. {
  2340. return CachedDependencyInfo->AssetStatus != EDependencyNodeStatus::Valid;
  2341. }
  2342. else
  2343. {
  2344. return CachedDependencyInfo->AssetStatus != EDependencyNodeStatus::Valid && CachedDependencyInfo->AssetStatus != EDependencyNodeStatus::ValidWithSoftReferenceIssue;
  2345. }
  2346. }
  2347. return false;
  2348. }
  2349. FExtAssetData* FExtAssetRegistry::GetOrCacheAssetByFilePath(const FName& InFilePath, bool bDelayParse/* = false*/)
  2350. {
  2351. auto& CachedAssetsByFilePath = State.CachedAssetsByFilePath;
  2352. if (!CachedAssetsByFilePath.Contains(InFilePath))
  2353. {
  2354. if (!IFileManager::Get().FileExists(*InFilePath.ToString()))
  2355. {
  2356. return nullptr;
  2357. }
  2358. // New Asset Data
  2359. FExtAssetData* NewAssetData = new FExtAssetData(InFilePath.ToString(), bDelayParse);
  2360. //if (NewAssetData->IsValid())
  2361. {
  2362. State.CacheNewAsset(NewAssetData);
  2363. }
  2364. return NewAssetData;
  2365. }
  2366. FExtAssetData** FoundAssetData = CachedAssetsByFilePath.Find(InFilePath);
  2367. if (FoundAssetData)
  2368. {
  2369. if (!bDelayParse && !((*FoundAssetData)->IsParsed()))
  2370. {
  2371. (*FoundAssetData)->Parse();
  2372. }
  2373. if ((*FoundAssetData)->IsParsed())
  2374. {
  2375. State.ReCacheParsedAsset(*FoundAssetData);
  2376. }
  2377. return *FoundAssetData;
  2378. }
  2379. return NULL;
  2380. }
  2381. TMap<FName, TArray<FExtAssetData*>>& FExtAssetRegistry::GetOrCacheAssetsByFolder(const FName& InFolder, bool bDelayParse)
  2382. {
  2383. auto& CachedAssetsByFolder = State.CachedAssetsByFolder;
  2384. if (!CachedAssetsByFolder.Contains(InFolder))
  2385. {
  2386. TArray<FExtAssetData*> Assets;
  2387. {
  2388. auto& CachedFilePathsByFolder = State.CachedFilePathsByFolder;
  2389. if (!CachedFilePathsByFolder.Contains(InFolder))
  2390. {
  2391. TArray<FName> PathList;
  2392. FExtPackageUtils::GetAllPackages(InFolder, PathList);
  2393. CachedFilePathsByFolder.Add(InFolder, PathList);
  2394. }
  2395. auto& PathList = CachedFilePathsByFolder[InFolder];
  2396. for (const auto& FilePath : PathList)
  2397. {
  2398. FExtAssetData* CachedAsset = GetOrCacheAssetByFilePath(FilePath, bDelayParse);
  2399. if (CachedAsset/* && CachedAsset->IsValid()*/)
  2400. {
  2401. Assets.Emplace(CachedAsset);
  2402. }
  2403. }
  2404. }
  2405. CachedAssetsByFolder.Add(InFolder, Assets);
  2406. }
  2407. return CachedAssetsByFolder;
  2408. }
  2409. TMap<FName, TArray<FExtAssetData*>>& FExtAssetRegistry::GetOrCacheAssetsByClass(const TSet<FName>& InPaths, bool bRecursively)
  2410. {
  2411. for (const FName& InPath : InPaths)
  2412. {
  2413. GetOrCacheAssetsByFolder(InPath);
  2414. if (bRecursively)
  2415. {
  2416. TSet<FName> SubPaths;
  2417. GetOrCacheSubPaths(InPath, SubPaths, /*bInRecurse =*/ true);
  2418. for (const FName& SubPath : SubPaths)
  2419. {
  2420. GetOrCacheAssetsByFolder(SubPath);
  2421. }
  2422. }
  2423. }
  2424. return State.CachedAssetsByClass;
  2425. }
  2426. FExtAssetData* FExtAssetRegistry::GetCachedAssetByFilePath(const FName& InFilePath)
  2427. {
  2428. auto& CachedAssetsByFilePath = State.CachedAssetsByFilePath;
  2429. FExtAssetData** FoundAssetData = CachedAssetsByFilePath.Find(InFilePath);
  2430. if (FoundAssetData)
  2431. {
  2432. return *FoundAssetData;
  2433. }
  2434. return NULL;
  2435. }
  2436. FExtAssetData* FExtAssetRegistry::GetCachedAssetByPackageName(const FName& InPackageName)
  2437. {
  2438. if (const FName* FilePath = State.CachedPackageNameToFilePathMap.Find(InPackageName))
  2439. {
  2440. return GetCachedAssetByFilePath(*FilePath);
  2441. }
  2442. return NULL;
  2443. }
  2444. TArray<FExtAssetData*> FExtAssetRegistry::GetCachedAssetsByFolder(const FName& InDirectory)
  2445. {
  2446. if (State.CachedAssetsByFolder.Find(InDirectory))
  2447. {
  2448. return State.CachedAssetsByFolder[InDirectory];
  2449. }
  2450. return TArray<FExtAssetData*>();
  2451. }
  2452. TArray<FExtAssetData*> FExtAssetRegistry::GetCachedAssetsByClass(const FName& InClass)
  2453. {
  2454. if (State.CachedAssetsByClass.Find(InClass))
  2455. {
  2456. return State.CachedAssetsByClass[InClass];
  2457. }
  2458. return TArray<FExtAssetData*>();
  2459. }
  2460. const TMap<FName, FName>& FExtAssetRegistry::GetCachedAssetContentRootConfigDirs() const
  2461. {
  2462. return State.CachedAssetContentRootConfigDirs;
  2463. }
  2464. const TMap<FName, FName>& FExtAssetRegistry::GetCachedAssetContentRootHostDirs() const
  2465. {
  2466. return State.CachedAssetContentRootHosts;
  2467. }
  2468. #if ECB_WIP_THUMB_CACHE
  2469. const FObjectThumbnail* FExtAssetRegistry::FindCachedThumbnail(const FName& InFilePath)
  2470. {
  2471. return State.CachedThumbnails.Find(InFilePath);
  2472. }
  2473. #endif
  2474. bool FExtAssetRegistry::IsEmptyFolder(const FString& FolderPath)
  2475. {
  2476. auto& CachedEmptyFolders = State.CachedEmptyFoldersStatus;
  2477. FName FolderName(*FolderPath);
  2478. if (CachedEmptyFolders.Contains(FolderName))
  2479. {
  2480. return CachedEmptyFolders[FolderName];
  2481. }
  2482. bool bEmptyFolder = !FExtPackageUtils::HasPackages(FolderPath, /*bRecursively*/ true);
  2483. // cache it
  2484. CachedEmptyFolders.Add(FolderName, bEmptyFolder);
  2485. return bEmptyFolder;
  2486. }
  2487. bool FExtAssetRegistry::IsFolderAssetsFullyParsed(const FString& FolderPath) const
  2488. {
  2489. const auto& CachedFullyParsedFolders = State.CachedFullyParsedFolders;
  2490. return CachedFullyParsedFolders.Contains(*FolderPath);
  2491. }
  2492. bool FExtAssetRegistry::CombineRootContentPaths()
  2493. {
  2494. return FContentPathUtil::CombineAndSortRootContentPaths(RootContentPaths, RootContentPaths) > 0;
  2495. }
  2496. void FExtAssetRegistry::CacheRootContentPathInfo()
  2497. {
  2498. const bool bHideContentName = true;
  2499. RootContentPathsInfo.Empty();
  2500. for (const FString& RootContentPath : RootContentPaths)
  2501. {
  2502. FExtAssetData::EContentType ContentType = FExtAssetData::EContentType::Orphan;
  2503. // Strip off any trailing forward slashes
  2504. FString DisplayFolderPath = RootContentPath;
  2505. FPaths::NormalizeDirectoryName(DisplayFolderPath);
  2506. FString ContentFolderPath;
  2507. static const FString VaultCacheContentFolderName = TEXT("/data/Content");
  2508. static const FString VaultCacheDataFolderName = TEXT("/data");
  2509. static const FString ProjectOrPluginContentFolderName = TEXT("/Content");
  2510. FString TmpAssetContentRoot;
  2511. FString TmpRelativePath;
  2512. if (DisplayFolderPath.EndsWith(VaultCacheContentFolderName))
  2513. {
  2514. FString VaultCacheFolder = FPaths::ConvertRelativePathToFull(FPaths::Combine(DisplayFolderPath, TEXT("../..")));
  2515. if (FExtContentDirFinder::FindWithFolder(VaultCacheFolder, TEXT("manifest"), /*bExtension*/ false))
  2516. {
  2517. ContentFolderPath = DisplayFolderPath;
  2518. DisplayFolderPath = VaultCacheFolder;
  2519. ContentType = FExtAssetData::EContentType::VaultCache;
  2520. }
  2521. }
  2522. else if (DisplayFolderPath.EndsWith(VaultCacheDataFolderName))
  2523. {
  2524. FString VaultCacheFolder = FPaths::ConvertRelativePathToFull(FPaths::Combine(DisplayFolderPath, TEXT("..")));
  2525. if (FExtContentDirFinder::FindWithFolder(VaultCacheFolder, TEXT("manifest"), /*bExtension*/ false))
  2526. {
  2527. ContentFolderPath = DisplayFolderPath + TEXT("/Content");
  2528. DisplayFolderPath = VaultCacheFolder;
  2529. ContentType = FExtAssetData::EContentType::VaultCache;
  2530. }
  2531. }
  2532. else if (DisplayFolderPath.EndsWith(ProjectOrPluginContentFolderName))
  2533. {
  2534. ContentFolderPath = DisplayFolderPath;
  2535. FString ProjectOrPluginFolder = FPaths::ConvertRelativePathToFull(FPaths::Combine(DisplayFolderPath, TEXT("..")));
  2536. if (FExtContentDirFinder::FindWithFolder(ProjectOrPluginFolder, TEXT(".uplugin"), /*bExtension*/ true))
  2537. {
  2538. DisplayFolderPath = ProjectOrPluginFolder;
  2539. ContentType = FExtAssetData::EContentType::Plugin;
  2540. }
  2541. else if (FExtContentDirFinder::FindWithFolder(ProjectOrPluginFolder, TEXT(".uproject"), /*bExtension*/ true))
  2542. {
  2543. DisplayFolderPath = ProjectOrPluginFolder;
  2544. ContentType = FExtAssetData::EContentType::Project;
  2545. }
  2546. }
  2547. ////////////////////////////////////////////
  2548. //
  2549. else if (FExtContentDirFinder::FindWithFolder(DisplayFolderPath, TEXT("manifest"), /*bExtension*/ false))
  2550. {
  2551. ContentFolderPath = DisplayFolderPath + VaultCacheContentFolderName;
  2552. ContentType = FExtAssetData::EContentType::VaultCache;
  2553. }
  2554. else if (FExtContentDirFinder::FindWithFolder(DisplayFolderPath, TEXT(".uplugin"), /*bExtension*/ true))
  2555. {
  2556. ContentFolderPath = DisplayFolderPath + ProjectOrPluginContentFolderName;
  2557. ContentType = FExtAssetData::EContentType::Plugin;
  2558. }
  2559. else if (FExtContentDirFinder::FindWithFolder(DisplayFolderPath, TEXT(".uproject"), /*bExtension*/ true))
  2560. {
  2561. ContentFolderPath = DisplayFolderPath + ProjectOrPluginContentFolderName;
  2562. ContentType = FExtAssetData::EContentType::Project;
  2563. }
  2564. // Partial Project
  2565. if (ContentType == FExtAssetData::EContentType::Orphan && FExtContentDirFinder::FindFolder(DisplayFolderPath, TEXT("Content"), TmpAssetContentRoot, TmpRelativePath))
  2566. {
  2567. ContentFolderPath = TmpAssetContentRoot;
  2568. DisplayFolderPath = bHideContentName ? FPaths::ConvertRelativePathToFull(FPaths::Combine(ContentFolderPath, TEXT(".."))) : ContentFolderPath;
  2569. ContentType = FExtAssetData::EContentType::PartialProject;
  2570. }
  2571. FString DisplayFolderName = FPaths::GetPathLeaf(DisplayFolderPath);
  2572. FText LocalizedFolderName = DisplayFolderName.IsEmpty() ? FText::FromString(DisplayFolderPath) : FText::FromString(DisplayFolderName);
  2573. FPaths::NormalizeDirectoryName(ContentFolderPath);
  2574. FRootContentPathInfo PathInfo;
  2575. PathInfo.ContentType = ContentType;
  2576. PathInfo.AssetContentRoot = ContentFolderPath;
  2577. PathInfo.DisplayName = LocalizedFolderName;
  2578. RootContentPathsInfo.FindOrAdd(RootContentPath) = PathInfo;
  2579. }
  2580. }
  2581. void FExtAssetRegistry::CacheFolderColor(const FName& InAssetContentRoot)
  2582. {
  2583. if (const FName* ConfigDirName = State.CachedAssetContentRootConfigDirs.Find(InAssetContentRoot))
  2584. {
  2585. if (!ConfigDirName->IsNone())
  2586. {
  2587. TMap<FName, FLinearColor> FilePathColors;
  2588. if (FExtConfigUtil::LoadPathColors(InAssetContentRoot.ToString(), ConfigDirName->ToString(), FilePathColors))
  2589. {
  2590. TArray<FName> FolderPaths;
  2591. FilePathColors.GenerateKeyArray(FolderPaths);
  2592. State.CachedFolderColorIndices.FindOrAdd(InAssetContentRoot, FolderPaths);
  2593. State.CachedFolderColors.Append(FilePathColors);
  2594. }
  2595. }
  2596. }
  2597. }
  2598. void FExtAssetRegistry::GetSubClasses(const TArray<FTopLevelAssetPath>& InClassPaths, const TSet<FTopLevelAssetPath>& ExcludedClassNames, TSet<FTopLevelAssetPath>& SubClassNames) const
  2599. {
  2600. struct FClassCacheHelper
  2601. {
  2602. void UpdateTemporaryCaches()
  2603. {
  2604. // And add all in-memory classes at request time
  2605. TSet<FTopLevelAssetPath> InMemoryClassNames;
  2606. for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
  2607. {
  2608. UClass* Class = *ClassIt;
  2609. if (!Class->HasAnyClassFlags(CLASS_Deprecated | CLASS_NewerVersionExists))
  2610. {
  2611. FTopLevelAssetPath ClassPathName = Class->GetClassPathName();
  2612. if (Class->GetSuperClass())
  2613. {
  2614. FTopLevelAssetPath SuperClassName = Class->GetSuperClass()->GetClassPathName();
  2615. TSet<FTopLevelAssetPath>& ChildClasses = TempReverseInheritanceMap.FindOrAdd(SuperClassName);
  2616. ChildClasses.Add(ClassPathName);
  2617. TempCachedInheritanceMap.Add(ClassPathName, SuperClassName);
  2618. }
  2619. else
  2620. {
  2621. // This should only be true for a small number of CoreUObject classes
  2622. TempCachedInheritanceMap.Add(ClassPathName, FTopLevelAssetPath());
  2623. }
  2624. // Add any implemented interfaces to the reverse inheritance map, but not to the forward map
  2625. for (int32 i = 0; i < Class->Interfaces.Num(); ++i)
  2626. {
  2627. UClass* InterfaceClass = Class->Interfaces[i].Class;
  2628. if (InterfaceClass) // could be nulled out by ForceDelete of a blueprint interface
  2629. {
  2630. TSet<FTopLevelAssetPath>& ChildClasses = TempReverseInheritanceMap.FindOrAdd(InterfaceClass->GetClassPathName());
  2631. ChildClasses.Add(ClassPathName);
  2632. }
  2633. }
  2634. InMemoryClassNames.Add(ClassPathName);
  2635. }
  2636. }
  2637. // Add non-native classes to reverse map
  2638. for (auto ClassNameIt = TempCachedInheritanceMap.CreateConstIterator(); ClassNameIt; ++ClassNameIt)
  2639. {
  2640. const FTopLevelAssetPath& ClassName = ClassNameIt.Key();
  2641. if (!InMemoryClassNames.Contains(ClassName))
  2642. {
  2643. const FTopLevelAssetPath& ParentClassName = ClassNameIt.Value();
  2644. if (ParentClassName.IsNull())
  2645. {
  2646. TSet<FTopLevelAssetPath>& ChildClasses = TempReverseInheritanceMap.FindOrAdd(ParentClassName);
  2647. ChildClasses.Add(ClassName);
  2648. }
  2649. }
  2650. }
  2651. }
  2652. void ClearTemporaryCaches()
  2653. {
  2654. TempReverseInheritanceMap.Empty();
  2655. }
  2656. /** A temporary fully cached list including native classes */
  2657. TMap<FTopLevelAssetPath, FTopLevelAssetPath> TempCachedInheritanceMap;
  2658. /** A reverse map of TempCachedInheritanceMap, only kept during temp caching */
  2659. TMap<FTopLevelAssetPath, TSet<FTopLevelAssetPath>> TempReverseInheritanceMap;
  2660. } ClassCacheHelper;
  2661. ClassCacheHelper.UpdateTemporaryCaches();
  2662. for (auto& ClassPath : InClassPaths)
  2663. {
  2664. // Now find all subclass names
  2665. TSet<FTopLevelAssetPath> ProcessedClassNames;
  2666. GetSubClasses_Recursive(ClassPath, SubClassNames, ProcessedClassNames, ClassCacheHelper.TempReverseInheritanceMap, ExcludedClassNames);
  2667. }
  2668. ClassCacheHelper.ClearTemporaryCaches();
  2669. }
  2670. void FExtAssetRegistry::GetSubClasses_Recursive(const FTopLevelAssetPath& InClassName, TSet<FTopLevelAssetPath>& SubClassNames, TSet<FTopLevelAssetPath>& ProcessedClassNames, const TMap<FTopLevelAssetPath, TSet<FTopLevelAssetPath>>& ReverseInheritanceMap, const TSet<FTopLevelAssetPath>& ExcludedClassNames) const
  2671. {
  2672. if (ExcludedClassNames.Contains(InClassName))
  2673. {
  2674. // This class is in the exclusion list. Exclude it.
  2675. }
  2676. else if (ProcessedClassNames.Contains(InClassName))
  2677. {
  2678. // This class has already been processed. Ignore it.
  2679. }
  2680. else
  2681. {
  2682. SubClassNames.Add(InClassName);
  2683. ProcessedClassNames.Add(InClassName);
  2684. const TSet<FTopLevelAssetPath>* FoundSubClassNames = ReverseInheritanceMap.Find(InClassName);
  2685. if (FoundSubClassNames)
  2686. {
  2687. for (const FTopLevelAssetPath& ClassName : (*FoundSubClassNames))
  2688. {
  2689. GetSubClasses_Recursive(ClassName, SubClassNames, ProcessedClassNames, ReverseInheritanceMap, ExcludedClassNames);
  2690. }
  2691. }
  2692. }
  2693. }
  2694. void FExtAssetRegistry::StopAllBackgroundGathering()
  2695. {
  2696. if (BackgroundFolderGatherer.IsValid())
  2697. {
  2698. BackgroundFolderGatherer->EnsureCompletion();
  2699. BackgroundFolderGatherer.Reset();
  2700. }
  2701. if (BackgroundAssetGatherer.IsValid())
  2702. {
  2703. BackgroundAssetGatherer->AddBatchToGather(TArray<FName>());
  2704. BackgroundAssetGatherer->EnsureCompletion();
  2705. BackgroundAssetGatherer.Reset();
  2706. }
  2707. BackgroundGatheringSubFolder = NAME_None;
  2708. BackgroundGatheringFolders.Empty();
  2709. }
  2710. bool FExtAssetRegistry::IsFilterValid(const FARFilter& Filter, bool bAllowRecursion)
  2711. {
  2712. for (int32 NameIdx = 0; NameIdx < Filter.PackageNames.Num(); ++NameIdx)
  2713. {
  2714. if (Filter.PackageNames[NameIdx] == NAME_None)
  2715. {
  2716. return false;
  2717. }
  2718. }
  2719. for (int32 PathIdx = 0; PathIdx < Filter.PackagePaths.Num(); ++PathIdx)
  2720. {
  2721. if (Filter.PackagePaths[PathIdx] == NAME_None)
  2722. {
  2723. return false;
  2724. }
  2725. }
  2726. for (int32 ObjectPathIdx = 0; ObjectPathIdx < Filter.SoftObjectPaths.Num(); ++ObjectPathIdx)
  2727. {
  2728. if (Filter.SoftObjectPaths[ObjectPathIdx].IsNull())
  2729. {
  2730. return false;
  2731. }
  2732. }
  2733. for (int32 ClassIdx = 0; ClassIdx < Filter.ClassPaths.Num(); ++ClassIdx)
  2734. {
  2735. if (!Filter.ClassPaths[ClassIdx].IsValid())
  2736. {
  2737. return false;
  2738. }
  2739. }
  2740. for (auto FilterTagIt = Filter.TagsAndValues.CreateConstIterator(); FilterTagIt; ++FilterTagIt)
  2741. {
  2742. if (FilterTagIt.Key() == NAME_None)
  2743. {
  2744. return false;
  2745. }
  2746. }
  2747. if (!bAllowRecursion && (Filter.bRecursiveClasses || Filter.bRecursivePaths))
  2748. {
  2749. return false;
  2750. }
  2751. return true;
  2752. }
  2753. void FExtAssetRegistry::ExpandRecursiveFilter(const FARFilter& InFilter, FARFilter& ExpandedFilter)
  2754. {
  2755. TSet<FName> FilterPackagePaths;
  2756. TSet<FTopLevelAssetPath> FilterClassNames;
  2757. const int32 NumFilterPackagePaths = InFilter.PackagePaths.Num();
  2758. const int32 NumFilterClasses = InFilter.ClassPaths.Num();
  2759. ExpandedFilter = InFilter;
  2760. for (int32 PathIdx = 0; PathIdx < NumFilterPackagePaths; ++PathIdx)
  2761. {
  2762. FilterPackagePaths.Add(InFilter.PackagePaths[PathIdx]);
  2763. }
  2764. if (InFilter.bRecursivePaths)
  2765. {
  2766. // Add subpaths to all the input paths to the list
  2767. for (int32 PathIdx = 0; PathIdx < NumFilterPackagePaths; ++PathIdx)
  2768. {
  2769. GetOrCacheSubPaths(InFilter.PackagePaths[PathIdx], FilterPackagePaths, /*bRecursely = */ true);
  2770. }
  2771. }
  2772. ExpandedFilter.bRecursivePaths = false;
  2773. ExpandedFilter.PackagePaths = FilterPackagePaths.Array();
  2774. if (InFilter.bRecursiveClasses)
  2775. {
  2776. if (InFilter.RecursiveClassPathsExclusionSet.Num() > 0 && InFilter.ClassPaths.Num() == 0)
  2777. {
  2778. // Build list of all classes then remove excluded classes
  2779. TArray<FTopLevelAssetPath> ClassNamesObject;
  2780. ClassNamesObject.Add(FTopLevelAssetPath(TEXT("/Script/CoreUObject"), NAME_Class));
  2781. // GetSubClasses includes the base classes
  2782. GetSubClasses(ClassNamesObject, InFilter.RecursiveClassPathsExclusionSet, FilterClassNames);
  2783. }
  2784. else
  2785. {
  2786. // GetSubClasses includes the base classes
  2787. GetSubClasses(InFilter.ClassPaths, InFilter.RecursiveClassPathsExclusionSet, FilterClassNames);
  2788. }
  2789. }
  2790. else
  2791. {
  2792. for (int32 ClassIdx = 0; ClassIdx < NumFilterClasses; ++ClassIdx)
  2793. {
  2794. FilterClassNames.Add(InFilter.ClassPaths[ClassIdx]);
  2795. }
  2796. }
  2797. ExpandedFilter.ClassPaths = FilterClassNames.Array();
  2798. ExpandedFilter.bRecursiveClasses = false;
  2799. ExpandedFilter.RecursiveClassPathsExclusionSet.Empty();
  2800. }
  2801. TOptional<FAssetImportInfo> FExtAssetRegistry::ExtractAssetImportInfo(const FExtAssetData& AssetData) const
  2802. {
  2803. static const FName LegacySourceFilePathName("SourceFile");
  2804. FAssetDataTagMapSharedView::FFindTagResult Result = AssetData.TagsAndValues.FindTag(UObject::SourceFileTagName());
  2805. if (Result.IsSet())
  2806. {
  2807. return FAssetImportInfo::FromJson(Result.GetValue());
  2808. }
  2809. else
  2810. {
  2811. FAssetDataTagMapSharedView::FFindTagResult ResultLegacy = AssetData.TagsAndValues.FindTag(LegacySourceFilePathName);
  2812. if (ResultLegacy.IsSet())
  2813. {
  2814. FAssetImportInfo Legacy;
  2815. Legacy.Insert(ResultLegacy.GetValue());
  2816. return Legacy;
  2817. }
  2818. else
  2819. {
  2820. return TOptional<FAssetImportInfo>();
  2821. }
  2822. }
  2823. }
  2824. void FExtAssetRegistry::PrintCacheStatus()
  2825. {
  2826. ECB_LOG(Display, TEXT(" FExtObjectThumbnailPool"));
  2827. ECB_LOG(Display, TEXT(" ================================="));
  2828. ECB_LOG(Display, TEXT(" MaxNumThumbnailsInPool: %d"), FExtContentBrowserSingleton::GetThumbnailPool().NumInPool);
  2829. ECB_LOG(Display, TEXT(" Num Used in Pool: %d"), FExtContentBrowserSingleton::GetThumbnailPool().Pool.Num());
  2830. ECB_LOG(Display, TEXT(" Pool Memory Ussage: %dk"), FExtContentBrowserSingleton::GetThumbnailPool().GetAllocatedSize() / 1024);
  2831. ECB_LOG(Display, TEXT(" ---------------------------------"));
  2832. ECB_LOG(Display, TEXT(" FExtAssetRegistry"));
  2833. ECB_LOG(Display, TEXT(" ================================="));
  2834. ECB_LOG(Display, TEXT(" RootContentPaths: %d"), RootContentPaths.Num());
  2835. ECB_LOG(Display, TEXT(" BackgroundGatheringFolders: %d"), BackgroundGatheringFolders.Num());
  2836. ECB_LOG(Display, TEXT(" BackgroundGatheringSubFolder: %s"), *BackgroundGatheringSubFolder.ToString());
  2837. ECB_LOG(Display, TEXT(" ---------------------------------"));
  2838. State.PrintStatus();
  2839. }
  2840. void FExtAssetRegistry::ClearCache()
  2841. {
  2842. State.Reset();
  2843. }
  2844. void FExtAssetRegistry::UpdateCacheByRemovedFolders(const TArray<FString>& InRemovedFolders)
  2845. {
  2846. State.UpdateCacheByRemovedFolders(InRemovedFolders);
  2847. }
  2848. void FExtAssetRegistry::StartFolderGathering(const FString& InGatheringFolder)
  2849. {
  2850. FName ToGatherFolderName(*InGatheringFolder);
  2851. BackgroundGatheringFolders.Add(ToGatherFolderName);
  2852. #if ECB_WIP_IMPORT_ORPHAN
  2853. FString RootContentPath;
  2854. QueryRootContentPathFromFilePath(InGatheringFolder, RootContentPath);
  2855. ECB_LOG(Display, TEXT("[StartFolderGathering] %s, FoundRoot: %s"), *InGatheringFolder, *RootContentPath);
  2856. #endif
  2857. #if ECB_FEA_IGNORE_FOLDERS
  2858. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  2859. TArray<FString> IgnoreFolders = ExtContentBrowserSetting->IgnoreFolders;
  2860. if (ExtContentBrowserSetting->bIgnoreCommonNonContentFolders)
  2861. {
  2862. IgnoreFolders.Append(ExtContentBrowserSetting->CommonNonContentFolders);
  2863. }
  2864. if (ExtContentBrowserSetting->bIgnoreExternalContentFolders)
  2865. {
  2866. IgnoreFolders.Append(ExtContentBrowserSetting->ExternalContentFolders);
  2867. }
  2868. FExtFolderGatherPolicy GatherPolicy(ExtContentBrowserSetting->bIgnoreFoldersStartWithDot, IgnoreFolders);
  2869. #endif
  2870. if (!BackgroundFolderGatherer.IsValid())
  2871. {
  2872. #if ECB_FEA_IGNORE_FOLDERS
  2873. BackgroundFolderGatherer = MakeShareable(new FExtFolderGatherer(InGatheringFolder, GatherPolicy));
  2874. #else
  2875. BackgroundFolderGatherer = MakeShareable(new FExtFolderGatherer(InGatheringFolder));
  2876. #endif
  2877. }
  2878. else
  2879. {
  2880. #if ECB_FEA_IGNORE_FOLDERS
  2881. BackgroundFolderGatherer->AddSearchFolder(InGatheringFolder, &GatherPolicy);
  2882. #else
  2883. BackgroundFolderGatherer->AddSearchFolder(InGatheringFolder);
  2884. #endif
  2885. }
  2886. }
  2887. void FExtAssetRegistry::StartOrCancelAssetGathering(const TArray<FName>& InAssetPaths, int32 TotoalFiltered)
  2888. {
  2889. if (!BackgroundAssetGatherer.IsValid())
  2890. {
  2891. BackgroundAssetGatherer = MakeShareable(new FExtAssetGatherer());
  2892. }
  2893. BackgroundAssetGatherer->AddBatchToGather(InAssetPaths);
  2894. bIsGatheringAssets = InAssetPaths.Num() > 0;
  2895. NumFilteredAssets = TotoalFiltered;
  2896. NumToGatherAssets = InAssetPaths.Num();
  2897. }
  2898. //////////////////////////////////////////////////////
  2899. // FExtAssetDataUtil
  2900. //
  2901. #if 0
  2902. bool FExtAssetDataUtil::RemapPackagesToFullFilePath(const FString& InPackageName, const TArray<FString>& InRootPackageNamesToReplace, const FString& InAssetContentDir, FString& OutFullFilePath)
  2903. {
  2904. for (const FString& RootNameToFind/*RootNameToFind*/ : InRootPackageNamesToReplace)
  2905. {
  2906. //todo: make sure RootNameToFind ends with "/", e.g. TEXT("/Game/")
  2907. {
  2908. }
  2909. const int32 SkipLen = RootNameToFind.Len();
  2910. if (InPackageName.StartsWith(RootNameToFind))
  2911. {
  2912. FString RemappedFileBase = FPaths::Combine(InAssetContentDir, *FPaths::GetPath(InPackageName.Mid(SkipLen)), *FPaths::GetBaseFilename(InPackageName));
  2913. OutFullFilePath = RemappedFileBase + FExtAssetSupport::AssetPackageExtension;
  2914. if (IFileManager::Get().FileExists(*OutFullFilePath))
  2915. {
  2916. return true;
  2917. }
  2918. OutFullFilePath = RemappedFileBase + FExtAssetSupport::MapPackageExtension;
  2919. if (IFileManager::Get().FileExists(*OutFullFilePath))
  2920. {
  2921. return true;
  2922. }
  2923. }
  2924. }
  2925. return false;
  2926. }
  2927. #endif
  2928. FString FExtAssetDataUtil::GetPluginNameFromAssetContentRoot(const FString& InAssetContentRoot)
  2929. {
  2930. auto IsEndOfSearchChar = [](TCHAR C) { return (C == TEXT('/')) || (C == TEXT('\\')); };
  2931. int32 EndPos = InAssetContentRoot.FindLastCharByPredicate(IsEndOfSearchChar);
  2932. int32 StartPos = InAssetContentRoot.FindLastCharByPredicate(IsEndOfSearchChar, EndPos) + 1;
  2933. FString Result = InAssetContentRoot.Mid(StartPos, EndPos - StartPos);
  2934. return FString::Printf(TEXT("/%s/"), *Result);
  2935. }
  2936. FString FExtAssetDataUtil::GetPackageRootFromFullPackagePath(const FString& InFullPackagePath)
  2937. {
  2938. auto IsEndOfSearchChar = [](TCHAR C) { return (C == TEXT('/')) || (C == TEXT('\\')); };
  2939. int32 StartPos = InFullPackagePath.Find(TEXT("/"), ESearchCase::CaseSensitive);
  2940. if (StartPos == 0)
  2941. {
  2942. int32 EndPos = InFullPackagePath.Find(TEXT("/"), ESearchCase::CaseSensitive, ESearchDir::FromStart, 1);
  2943. if (EndPos > 2)
  2944. {
  2945. FString Result = InFullPackagePath.Mid(1, EndPos - StartPos - 1);
  2946. return FString::Printf(TEXT("/%s/"), *Result);
  2947. }
  2948. }
  2949. static FString EmptyString(TEXT(""));
  2950. return EmptyString;
  2951. }
  2952. bool FExtAssetDataUtil::RemapGamePackageToFullFilePath(const FString& GameRoot, const FString& InPackageName, const FString& InAssetContentDir, FString& OutFullFilePath, bool bPackageIsFolder)
  2953. {
  2954. //static FString GameRoot(TEXT("/Game/"));
  2955. const int32 SkipLen = GameRoot.Len();
  2956. if (InPackageName.StartsWith(GameRoot))
  2957. {
  2958. FString RemappedFileBase = FPaths::Combine(InAssetContentDir, *FPaths::GetPath(InPackageName.Mid(SkipLen)), *FPaths::GetBaseFilename(InPackageName));
  2959. if (!bPackageIsFolder)
  2960. {
  2961. FString AssetFile = RemappedFileBase + FExtAssetSupport::AssetPackageExtension;;
  2962. OutFullFilePath = RemappedFileBase + FExtAssetSupport::AssetPackageExtension;
  2963. if (IFileManager::Get().FileExists(*AssetFile))
  2964. {
  2965. return true;
  2966. }
  2967. FString MapFile = RemappedFileBase + FExtAssetSupport::MapPackageExtension;;
  2968. if (IFileManager::Get().FileExists(*MapFile))
  2969. {
  2970. OutFullFilePath = MapFile;
  2971. return true;
  2972. }
  2973. }
  2974. else
  2975. {
  2976. OutFullFilePath = RemappedFileBase;
  2977. if (IFileManager::Get().DirectoryExists(*OutFullFilePath))
  2978. {
  2979. return true;
  2980. }
  2981. }
  2982. }
  2983. return false;
  2984. }
  2985. bool FExtAssetDataUtil::SyncToContentBrowser(const FExtAssetData& InAssetData)
  2986. {
  2987. if (InAssetData.HasContentRoot())
  2988. {
  2989. FString AssetFileRelativePath = InAssetData.AssetRelativePath.ToString();
  2990. FString AssetContentDir = InAssetData.AssetContentRoot.ToString();
  2991. FString ProjectContentDir = FPaths::ProjectContentDir();
  2992. FString RemappedFilePath = FPaths::Combine(ProjectContentDir, AssetFileRelativePath);
  2993. ECB_LOG(Display, TEXT("[SyncToContentBrowser] ProjectContentDir: %s => %s relative to %s => %s")
  2994. , *FPaths::ProjectContentDir(), *AssetFileRelativePath, *AssetContentDir, *RemappedFilePath);
  2995. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  2996. IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
  2997. TArray<FAssetData> AssetDatas;
  2998. AssetRegistry.GetAssetsByPackageName(*FPackageName::FilenameToLongPackageName(RemappedFilePath), AssetDatas, /*bIncludeOnlyOnDiskAssets*/false);
  2999. if (AssetDatas.Num() > 0 && AssetDatas[0].IsValid())
  3000. {
  3001. // Sync Assets in Content Browser
  3002. ECB_LOG(Display, TEXT("Syncing %d assets in content browser."), AssetDatas.Num());
  3003. {
  3004. FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
  3005. ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas);
  3006. }
  3007. return true;
  3008. }
  3009. }
  3010. return false;
  3011. }
  3012. /////////////////////////////////////////////////////////////
  3013. // FExtAssetDependencyWalker and support class
  3014. //
  3015. bool FExtAssetDependencyInfo::IsValid(bool bIgnoreSoftIssue)
  3016. {
  3017. // return InvalidDependentFiles.Num() == 0
  3018. // && MissingDependentFiles.Num() == 0
  3019. // && MissingPackageNames.Num() == 0;
  3020. return AssetStatus == EDependencyNodeStatus::Valid || (bIgnoreSoftIssue ? AssetStatus == EDependencyNodeStatus::ValidWithSoftReferenceIssue : true);
  3021. }
  3022. void FExtAssetDependencyInfo::Print()
  3023. {
  3024. ECB_LOG(Display, TEXT("--------------- ExtAssetDependencyInfo: totoal %d packages ----------"), AllDepdencyPackages.Num());
  3025. int i = 0;
  3026. for (const auto& Pair : AllDepdencyPackages)
  3027. {
  3028. FString Status = FExtAssetDependencyNode::GetStatusString(Pair.Value);
  3029. ECB_LOG(Display, TEXT(" %d)%s : %s"), i, *Status, *Pair.Key.ToString());
  3030. }
  3031. ECB_LOG(Display, TEXT("--------------- ExtAssetDependencyInfo: totoal %d AssetDepdencies ----------"), AssetDepdencies.Num());
  3032. for (const auto& Pair : AssetDepdencies)
  3033. {
  3034. ECB_LOG(Display, TEXT("- %s"), *Pair.Key.ToString());
  3035. const TArray<FExtAssetDependencyNode>& Nodes = Pair.Value;
  3036. i = 0;
  3037. for (const FExtAssetDependencyNode& Node : Nodes)
  3038. {
  3039. FString NodeString = Node.ToString();
  3040. ECB_LOG(Display, TEXT(" %d) %s"), i, *NodeString);
  3041. ++i;
  3042. }
  3043. }
  3044. }
  3045. FString FExtAssetDependencyNode::GetStatusString(EDependencyNodeStatus InStatus)
  3046. {
  3047. FString Status;
  3048. if (InStatus == EDependencyNodeStatus::Valid)
  3049. {
  3050. Status = TEXT("Valid");
  3051. }
  3052. else if (InStatus == EDependencyNodeStatus::ValidWithSoftReferenceIssue)
  3053. {
  3054. Status = TEXT("ValidWithSoftReferenceIssue");
  3055. }
  3056. else if (InStatus == EDependencyNodeStatus::Invalid)
  3057. {
  3058. Status = TEXT("Invalid");
  3059. }
  3060. else if (InStatus == EDependencyNodeStatus::Missing)
  3061. {
  3062. Status = TEXT("Missing");
  3063. }
  3064. else if (InStatus == EDependencyNodeStatus::AbortGathering)
  3065. {
  3066. Status = TEXT("Aborted");
  3067. }
  3068. else
  3069. {
  3070. Status = TEXT("Unknown");
  3071. }
  3072. return Status;
  3073. }
  3074. FString FExtAssetDependencyNode::ToString() const
  3075. {
  3076. FString ReferenceTypeString = ReferenceType == EDependencyNodeReferenceType::Soft ? TEXT("Soft") : TEXT("Hard");
  3077. FString StatusString = GetStatusString(NodeStatus);
  3078. return FString::Printf(TEXT("%s-%s: %s"), *ReferenceTypeString, *StatusString, *PackageName.ToString());
  3079. }
  3080. EDependencyNodeStatus FExtAssetDependencyWalker::GatherDependencies(const FExtAssetData& InAssetData, const FString& InAssetContentDir, const FExtAssetData::EContentType& InAssetContentType, FExtAssetDependencyInfo& InOuDependencyInfo, int32 Level, FScopedSlowTask* SlowTaskPtr)
  3081. {
  3082. FString Padding;
  3083. #if ECB_DEBUG
  3084. Padding = Padding.LeftPad((Level + 1) * 6) + TEXT("|");
  3085. #endif
  3086. EDependencyNodeStatus CurrentAssetStatus = EDependencyNodeStatus::Valid;
  3087. FString AssetFilePath = InAssetData.PackageFilePath.ToString();
  3088. ECB_LOG(Display, TEXT("%sGather dependencies for %s"), *Padding, *AssetFilePath);
  3089. const bool bFileExist = IFileManager::Get().FileExists(*AssetFilePath);
  3090. if (bFileExist)
  3091. {
  3092. if (InAssetData.IsValid())
  3093. {
  3094. InOuDependencyInfo.ValidDependentFiles.AddUnique(AssetFilePath);
  3095. }
  3096. else
  3097. {
  3098. InOuDependencyInfo.InvalidDependentFiles.AddUnique(AssetFilePath);
  3099. ECB_LOG(Error, TEXT("%s => %s Found but invalid: %s"), *Padding, *AssetFilePath, *InAssetData.GetInvalidReason());
  3100. return EDependencyNodeStatus::Invalid;
  3101. }
  3102. }
  3103. else
  3104. {
  3105. InOuDependencyInfo.MissingDependentFiles.AddUnique(AssetFilePath);
  3106. ECB_LOG(Display, TEXT("%s Missing!"), *Padding);
  3107. return EDependencyNodeStatus::Missing;
  3108. }
  3109. const FText ProgressFormat = LOCTEXT("ValidatingPackages", "Validating {0}...{1}");
  3110. ECB_LOG(Display, TEXT("%s Total Depends: (%d)"), *Padding, InAssetData.HardDependentPackages.Num() + InAssetData.SoftReferencesList.Num());
  3111. ECB_LOG(Display, TEXT("%s Hard Depends: (%d)"), *Padding, InAssetData.HardDependentPackages.Num());
  3112. for (const FName& DependPackageName : InAssetData.HardDependentPackages)
  3113. {
  3114. EDependencyNodeStatus PackageStatus = GatherPackageDependencies(DependPackageName, InOuDependencyInfo, InAssetContentDir, InAssetContentType, Level, /*bSoftReference*/ false, SlowTaskPtr);
  3115. if (PackageStatus == EDependencyNodeStatus::AbortGathering)
  3116. {
  3117. return EDependencyNodeStatus::AbortGathering;
  3118. }
  3119. TArray<FExtAssetDependencyNode>& DepdencyNodes = InOuDependencyInfo.AssetDepdencies.FindOrAdd(InAssetData.PackageFilePath);
  3120. DepdencyNodes.Emplace(DependPackageName, PackageStatus, /*bSoftReference*/ false);
  3121. if (SlowTaskPtr)
  3122. {
  3123. if (SlowTaskPtr->ShouldCancel())
  3124. {
  3125. return EDependencyNodeStatus::AbortGathering;
  3126. }
  3127. const float ProgessAmount = (Level == 0) ? 1.f : 0.f;
  3128. FName ShortPackageName = FPackageName::GetShortFName(DependPackageName);
  3129. const FText ProgessText = FText::Format(ProgressFormat, FText::FromName(ShortPackageName), FText::FromString(FExtAssetDependencyNode::GetStatusString(PackageStatus)));
  3130. SlowTaskPtr->EnterProgressFrame(ProgessAmount, ProgessText);
  3131. }
  3132. if ((PackageStatus == EDependencyNodeStatus::Invalid || PackageStatus == EDependencyNodeStatus::Missing)
  3133. && CurrentAssetStatus != EDependencyNodeStatus::Invalid)
  3134. {
  3135. CurrentAssetStatus = EDependencyNodeStatus::Invalid;
  3136. }
  3137. else if (PackageStatus == EDependencyNodeStatus::ValidWithSoftReferenceIssue
  3138. && CurrentAssetStatus == EDependencyNodeStatus::Valid)
  3139. {
  3140. CurrentAssetStatus = EDependencyNodeStatus::ValidWithSoftReferenceIssue;
  3141. }
  3142. }
  3143. ECB_LOG(Display, TEXT("%s Soft Depends: (%d)"), *Padding, InAssetData.SoftReferencesList.Num());
  3144. for (const FName& DependPackageName : InAssetData.SoftReferencesList)
  3145. {
  3146. EDependencyNodeStatus PackageStatus = GatherPackageDependencies(DependPackageName, InOuDependencyInfo, InAssetContentDir, InAssetContentType, Level, /*bSoftReference*/ true);
  3147. TArray<FExtAssetDependencyNode>& DepdencyNodes = InOuDependencyInfo.AssetDepdencies.FindOrAdd(InAssetData.PackageFilePath);
  3148. if (PackageStatus == EDependencyNodeStatus::AbortGathering)
  3149. {
  3150. return EDependencyNodeStatus::AbortGathering;
  3151. }
  3152. DepdencyNodes.Emplace(DependPackageName, PackageStatus, /*bSoftReference*/ true);
  3153. if (SlowTaskPtr)
  3154. {
  3155. if (SlowTaskPtr->ShouldCancel())
  3156. {
  3157. return EDependencyNodeStatus::AbortGathering;
  3158. }
  3159. const float ProgessAmount = (Level == 0) ? 1.f : 0.f;
  3160. FName ShortPackageName = FPackageName::GetShortFName(DependPackageName);
  3161. const FText ProgessText = FText::Format(ProgressFormat, FText::FromName(ShortPackageName), FText::FromString(FExtAssetDependencyNode::GetStatusString(PackageStatus)));
  3162. SlowTaskPtr->EnterProgressFrame(ProgessAmount, ProgessText);
  3163. }
  3164. if ((PackageStatus == EDependencyNodeStatus::Invalid || PackageStatus == EDependencyNodeStatus::Missing || PackageStatus == EDependencyNodeStatus::ValidWithSoftReferenceIssue)
  3165. && CurrentAssetStatus == EDependencyNodeStatus::Valid)
  3166. {
  3167. CurrentAssetStatus = EDependencyNodeStatus::ValidWithSoftReferenceIssue;
  3168. }
  3169. }
  3170. ECB_LOG(Display, TEXT("%s => Asset Status: %s"), *Padding, *FExtAssetDependencyNode::GetStatusString(CurrentAssetStatus));
  3171. return CurrentAssetStatus;
  3172. }
  3173. EDependencyNodeStatus FExtAssetDependencyWalker::GatherDependencies(const FExtAssetData& InAssetData, const FString& InAssetContentDir, const FExtAssetData::EContentType& InAssetContentType, FExtAssetDependencyInfo& InOuDependencyInfo, bool bShowProgess)
  3174. {
  3175. if (InAssetData.IsValid())
  3176. {
  3177. int32 NumDirectDependencies = InAssetData.HardDependentPackages.Num() + InAssetData.SoftReferencesList.Num();
  3178. FScopedSlowTask SlowTask(NumDirectDependencies + 1, LOCTEXT("GatheringDependencies", "Gathering dependencies..."));
  3179. int32 Level = 0;
  3180. FScopedSlowTask* SlowTaskPtr = bShowProgess ? &SlowTask : nullptr;
  3181. if (bShowProgess)
  3182. {
  3183. SlowTask.MakeDialog(/*ShowCancel*/true);
  3184. }
  3185. return GatherDependencies(InAssetData, InAssetContentDir, InAssetContentType, InOuDependencyInfo, Level, SlowTaskPtr);
  3186. }
  3187. return EDependencyNodeStatus::Invalid;
  3188. }
  3189. EDependencyNodeStatus FExtAssetDependencyWalker::GatherPackageDependencies(const FName& DependPackageName, FExtAssetDependencyInfo& InOuDependencyInfo, const FString& InAssetContentDir, const FExtAssetData::EContentType& InAssetContentType, int32& Level, bool bSoftReference, FScopedSlowTask* SlowTaskPtr)
  3190. {
  3191. if (SlowTaskPtr && SlowTaskPtr->ShouldCancel())
  3192. {
  3193. return EDependencyNodeStatus::AbortGathering;
  3194. }
  3195. FString Padding;
  3196. #if ECB_DEBUG
  3197. Padding = Padding.LeftPad((Level + 1) * 6) + (bSoftReference ? TEXT("|") : TEXT("||"));
  3198. #endif
  3199. FString DependPackageString = DependPackageName.ToString();
  3200. if (InOuDependencyInfo.AllDepdencyPackages.Contains(DependPackageName))
  3201. {
  3202. ECB_LOG(Display, TEXT("%s - %s (processed)"), *Padding, *DependPackageString);
  3203. return InOuDependencyInfo.AllDepdencyPackages[DependPackageName];
  3204. }
  3205. InOuDependencyInfo.AllDepdencyPackages.Add(DependPackageName, EDependencyNodeStatus::Valid);
  3206. EDependencyNodeStatus PackageStatus = EDependencyNodeStatus::Valid;
  3207. const FString UAssetExtension = FExtAssetSupport::AssetPackageExtension; // const FString UMapExtension = FExtAssetSupport::MapPackageExtension;// (TEXT(".uasset"));
  3208. FString GameRoot(TEXT("/Game/"));
  3209. #if ECB_FEA_IMPORT_PLUGIN
  3210. if (InAssetContentType == FExtAssetData::EContentType::Plugin && !InAssetContentDir.IsEmpty())
  3211. {
  3212. GameRoot = FExtAssetDataUtil::GetPluginNameFromAssetContentRoot(InAssetContentDir);
  3213. }
  3214. #endif
  3215. if (DependPackageString.StartsWith(GameRoot))
  3216. {
  3217. ECB_LOG(Display, TEXT("%s - %s (Gathering Dependencies)"), *Padding, *DependPackageString);
  3218. FString DependentFilePath;
  3219. if (!FExtAssetDataUtil::RemapGamePackageToFullFilePath(GameRoot, DependPackageString, InAssetContentDir, DependentFilePath))
  3220. {
  3221. InOuDependencyInfo.MissingDependentFiles.Add(DependentFilePath);
  3222. ECB_LOG(Error, TEXT(" %s => %s Missing!"), *Padding, *DependPackageString);
  3223. PackageStatus = EDependencyNodeStatus::Missing;
  3224. }
  3225. else
  3226. {
  3227. if (FExtAssetData* DependentAssetPtr = FExtContentBrowserSingleton::GetAssetRegistry().GetOrCacheAssetByFilePath(FName(*DependentFilePath)))
  3228. {
  3229. FExtAssetData& DependentAsset = *DependentAssetPtr;
  3230. PackageStatus = GatherDependencies(DependentAsset, InAssetContentDir, InAssetContentType, InOuDependencyInfo, Level + 1, SlowTaskPtr);
  3231. }
  3232. else
  3233. {
  3234. InOuDependencyInfo.InvalidDependentFiles.AddUnique(DependentFilePath);
  3235. ECB_LOG(Error, TEXT(" %s => %s Found but invalid!"), *Padding, *DependPackageString);
  3236. PackageStatus = EDependencyNodeStatus::Invalid;
  3237. }
  3238. }
  3239. }
  3240. else
  3241. {
  3242. const bool bPackageExist = FExtPackageUtils::DoesPackageExist(DependPackageString);
  3243. if (!bPackageExist)
  3244. {
  3245. InOuDependencyInfo.MissingPackageNames.Add(DependPackageString);
  3246. PackageStatus = EDependencyNodeStatus::Missing;
  3247. }
  3248. ECB_LOG(Display, TEXT("%s - FindPackage %s : %d"), *Padding, *DependPackageString, bPackageExist);
  3249. }
  3250. InOuDependencyInfo.AllDepdencyPackages[DependPackageName] = PackageStatus;
  3251. return PackageStatus;
  3252. }
  3253. //--------------------------------------------------
  3254. // FExtAssetValidator and support classes Impl
  3255. //
  3256. bool FExtAssetValidator::ValidateDependency(const TArray<FExtAssetData>& InAssetDatas, FString* OutValidateResult, bool bShowProgess, EDependencyNodeStatus* OutAssetStatus)
  3257. {
  3258. int32 NumValid = 0;
  3259. int32 NumInValid = 0;
  3260. EDependencyNodeStatus Result = EDependencyNodeStatus::Unknown;
  3261. for (const FExtAssetData& InAssetData : InAssetDatas)
  3262. {
  3263. bool bValid = false;
  3264. if (!InAssetData.IsValid() || !InAssetData.HasContentRoot())
  3265. {
  3266. #if 0
  3267. if (OutValidateResult)
  3268. {
  3269. if (!InAssetData.IsValid())
  3270. {
  3271. *OutValidateResult = FString::Printf(TEXT("Not a valid asset file: %s"), *InAssetData.GetInvalidReason());
  3272. }
  3273. else
  3274. {
  3275. *OutValidateResult = TEXT("The uasset file should be inside project content folder or vault cache data folder to be validate.");
  3276. }
  3277. }
  3278. #endif
  3279. if (!InAssetData.IsUAsset())
  3280. {
  3281. const bool bFileExist = IFileManager::Get().FileExists(*InAssetData.PackageFilePath.ToString());
  3282. bValid = bFileExist;
  3283. Result = bValid ? EDependencyNodeStatus::Valid : EDependencyNodeStatus::Missing;
  3284. }
  3285. else
  3286. {
  3287. bValid = InAssetData.IsValid();
  3288. Result = bValid ? EDependencyNodeStatus::Valid : EDependencyNodeStatus::Invalid;
  3289. }
  3290. }
  3291. else
  3292. {
  3293. FExtAssetDependencyInfo DependencyInfo = FExtContentBrowserSingleton::GetAssetRegistry().GetOrCacheAssetDependencyInfo(InAssetData, bShowProgess);
  3294. ECB_LOG(Display, TEXT("[ValidateDependency]%s => Asset Status: %s")
  3295. , *InAssetData.PackageFilePath.ToString()
  3296. , *FExtAssetDependencyNode::GetStatusString(DependencyInfo.AssetStatus));
  3297. Result = DependencyInfo.AssetStatus;
  3298. if (DependencyInfo.AssetStatus == EDependencyNodeStatus::AbortGathering)
  3299. {
  3300. if (OutValidateResult)
  3301. {
  3302. FString ErrorMessage = TEXT("Validation Aborted!");
  3303. *OutValidateResult = ErrorMessage;
  3304. }
  3305. bValid = false;
  3306. break;
  3307. }
  3308. else if (DependencyInfo.IsValid())
  3309. {
  3310. bValid = true;
  3311. }
  3312. else
  3313. {
  3314. bValid = false;
  3315. }
  3316. {
  3317. if (InAssetDatas.Num() == 1)
  3318. {
  3319. FString AssetContentDir = InAssetData.AssetContentRoot.ToString();
  3320. FString ErrorMessage = bValid
  3321. ? ((DependencyInfo.AssetStatus == EDependencyNodeStatus::ValidWithSoftReferenceIssue)
  3322. ? TEXT("Valid with soft refrences issues:\n\n")
  3323. : TEXT("Valid!\n\n"))
  3324. : TEXT("Invalid:\n\n");
  3325. if (DependencyInfo.MissingDependentFiles.Num() > 0)
  3326. {
  3327. FString WordEnd = DependencyInfo.MissingDependentFiles.Num() == 1 ? TEXT("") : TEXT("s");
  3328. FString Msg = FString::Printf(TEXT("Missing dependency file%s:\n"), *WordEnd);
  3329. FString AssetContentDirWithSlash = AssetContentDir + TEXT("/");
  3330. for (const FString& MissingFile : DependencyInfo.MissingDependentFiles)
  3331. {
  3332. FString FriendlyName = MissingFile;
  3333. FriendlyName.RemoveFromStart(AssetContentDirWithSlash);
  3334. Msg += FString::Printf(TEXT(" - %s\n"), *FriendlyName);
  3335. }
  3336. ErrorMessage += Msg;
  3337. }
  3338. if (DependencyInfo.InvalidDependentFiles.Num() > 0)
  3339. {
  3340. FString WordEnd = DependencyInfo.InvalidDependentFiles.Num() == 1 ? TEXT("") : TEXT("s");
  3341. FString Msg = FString::Printf(TEXT("\nInvalid dependency file%s found:\n"), *WordEnd);
  3342. for (const FString& InvalidFile : DependencyInfo.InvalidDependentFiles)
  3343. {
  3344. Msg += FString::Printf(TEXT(" - %s\n"), *InvalidFile);
  3345. }
  3346. ErrorMessage += Msg;
  3347. }
  3348. if (DependencyInfo.MissingPackageNames.Num() > 0)
  3349. {
  3350. FString WordEnd = DependencyInfo.MissingPackageNames.Num() == 1 ? TEXT("") : TEXT("s");
  3351. FString Msg = FString::Printf(TEXT("\nMissing dependency package%s:\n"), *WordEnd);
  3352. for (const FString& MissingPackage : DependencyInfo.MissingPackageNames)
  3353. {
  3354. Msg += FString::Printf(TEXT(" - %s\n"), *MissingPackage);
  3355. }
  3356. ErrorMessage += Msg;
  3357. }
  3358. if (OutValidateResult)
  3359. {
  3360. *OutValidateResult = ErrorMessage;
  3361. }
  3362. return bValid;
  3363. }
  3364. }
  3365. #if ECB_DEBUG
  3366. DependencyInfo.Print();
  3367. #endif
  3368. }
  3369. bValid ? ++NumValid : ++NumInValid;
  3370. }
  3371. const bool bAllValid = (NumValid == InAssetDatas.Num());
  3372. if (OutValidateResult)
  3373. {
  3374. if (bAllValid)
  3375. {
  3376. *OutValidateResult = TEXT("All dependencies are valid.");
  3377. }
  3378. else
  3379. {
  3380. *OutValidateResult = FString::Printf(TEXT("%d valid. %d invalid."), NumValid, NumInValid);
  3381. }
  3382. }
  3383. if (OutAssetStatus)
  3384. {
  3385. *OutAssetStatus = Result;
  3386. }
  3387. return bAllValid;
  3388. }
  3389. bool FExtAssetValidator::ValidateDependency(const TArray<FExtAssetData*>& InAssetDatas, FString* OutValidateResultPtr /*= nullptr*/, bool bShowProgess /*= false*/, EDependencyNodeStatus* OutAssetStatus /*= nullptr*/)
  3390. {
  3391. TArray<FExtAssetData> AssetDatas;
  3392. for (const FExtAssetData* AssetDataPtr : InAssetDatas)
  3393. {
  3394. AssetDatas.Emplace(*AssetDataPtr);
  3395. }
  3396. return ValidateDependency(AssetDatas, OutValidateResultPtr, bShowProgess, OutAssetStatus);
  3397. }
  3398. void FExtAssetValidator::InValidateDependency(const TArray<FExtAssetData>& InAssetDatas)
  3399. {
  3400. for (const FExtAssetData& InAssetData : InAssetDatas)
  3401. {
  3402. FExtContentBrowserSingleton::GetAssetRegistry().RemoveCachedAssetDependencyInfo(InAssetData);
  3403. }
  3404. }
  3405. //--------------------------------------------------
  3406. // FExtCollectionUtil Impl
  3407. //
  3408. FName FExtCollectionUtil::GetOrCreateCollection(FName InCollectionName, bool bUniqueCollection)
  3409. {
  3410. const ECollectionShareType::Type ShareType = ECollectionShareType::CST_Local;
  3411. FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
  3412. ICollectionManager& CollectionManager = CollectionManagerModule.Get();
  3413. FName CollectionName = InCollectionName == NAME_None ? FExtAssetContants::DefaultImportedUAssetCollectionName : InCollectionName;
  3414. ECB_LOG(Display, TEXT("[GetOrCreateCollection] %s %d"), *InCollectionName.ToString(), bUniqueCollection);
  3415. if (bUniqueCollection)
  3416. {
  3417. ECB_LOG(Display, TEXT("[GetOrCreateCollection] %s exist? %d"), *CollectionName.ToString(), CollectionManager.CollectionExists(CollectionName, ShareType));
  3418. if (!CollectionManager.CollectionExists(CollectionName, ShareType))
  3419. {
  3420. if (CollectionManager.CreateCollection(CollectionName, ShareType, ECollectionStorageMode::Static))
  3421. {
  3422. return CollectionName;
  3423. }
  3424. }
  3425. FName UniqueCollectionName;
  3426. CollectionManager.CreateUniqueCollectionName(CollectionName, ShareType, UniqueCollectionName);
  3427. if (CollectionManager.CreateCollection(UniqueCollectionName, ShareType, ECollectionStorageMode::Static))
  3428. {
  3429. ECB_LOG(Display, TEXT("[GetOrCreateCollection] %s exist? %d"), *UniqueCollectionName.ToString(), CollectionManager.CollectionExists(UniqueCollectionName, ShareType));
  3430. return UniqueCollectionName;
  3431. }
  3432. return UniqueCollectionName;
  3433. }
  3434. else
  3435. {
  3436. if (CollectionManager.CollectionExists(CollectionName, ShareType))
  3437. {
  3438. return CollectionName;
  3439. }
  3440. ECB_LOG(Display, TEXT("[GetOrCreateCollection] CreateCollection %s exist? %d"), *CollectionName.ToString(), CollectionManager.CollectionExists(CollectionName, ShareType));
  3441. if (CollectionManager.CreateCollection(CollectionName, ShareType, ECollectionStorageMode::Static))
  3442. {
  3443. return CollectionName;
  3444. }
  3445. }
  3446. return NAME_None;
  3447. }
  3448. bool FExtCollectionUtil::AddAssetsToCollection(const TArray<FAssetData>& InAssets, FName InCollectionName, bool bUniqueCollection)
  3449. {
  3450. FName CollectionName = GetOrCreateCollection(InCollectionName, bUniqueCollection);
  3451. if (CollectionName == NAME_None)
  3452. {
  3453. return false;
  3454. }
  3455. FCollectionManagerModule& CollectionManagerModule = FCollectionManagerModule::GetModule();
  3456. ICollectionManager& CollectionManager = CollectionManagerModule.Get();
  3457. TSet<FSoftObjectPath> CurrentAssetPaths;
  3458. for (const FAssetData& AssetData : InAssets)
  3459. {
  3460. CurrentAssetPaths.Add(AssetData.GetSoftObjectPath());
  3461. }
  3462. const TArray<FSoftObjectPath> ObjectPaths = CurrentAssetPaths.Array();
  3463. int32 NumAdded = 0;
  3464. ECollectionShareType::Type ShareType = ECollectionShareType::CST_Local;
  3465. if (CollectionManager.AddToCollection(CollectionName, ShareType, ObjectPaths, &NumAdded))
  3466. {
  3467. //if (NumAdded > 0)
  3468. {
  3469. return CollectionManager.SaveCollection(CollectionName, ShareType);
  3470. }
  3471. }
  3472. return false;
  3473. }
  3474. //--------------------------------------------------
  3475. // FExtAssetImporter and support classes Impl
  3476. //
  3477. struct FExtAssetImportTargetFolderInfo
  3478. {
  3479. FString TargetFolderPacakgeName;
  3480. FLinearColor TargetFolderColor;
  3481. bool bTargetFolderColorExist = false;
  3482. FLinearColor ExistingTargetFolderColor;
  3483. FExtAssetImportTargetFolderInfo(const FString& InTargetFolderPacakgeName, const FLinearColor& InTargetFolderColor)
  3484. : TargetFolderPacakgeName(InTargetFolderPacakgeName)
  3485. , TargetFolderColor(InTargetFolderColor)
  3486. {
  3487. TSharedPtr<FLinearColor> ExistingColor = ExtContentBrowserUtils::LoadColor(TargetFolderPacakgeName, /*bNoCache*/ true);
  3488. if (ExistingColor.IsValid())
  3489. {
  3490. bTargetFolderColorExist = true;
  3491. ExistingTargetFolderColor = *ExistingColor.Get();
  3492. }
  3493. }
  3494. };
  3495. struct FExtAssetImportTargetFileInfo
  3496. {
  3497. FString SourceFile;
  3498. FString TargetFile;
  3499. bool bTargeFileWasExist = false;
  3500. FName AssetContentDir = NAME_None;
  3501. FName SourceAssetName = NAME_None;
  3502. bool bSourceTargetHasSameClass = false;
  3503. bool bMainAsset = false;
  3504. bool bSkipped = false;
  3505. bool bRedirectMe = false; // Request to redirect
  3506. FString RedirectFromPackageName;
  3507. FString RedirectToPackageName;
  3508. // plugin?
  3509. bool bPluginAsset = false;
  3510. FString PluginRootWithSlash = TEXT("");
  3511. FExtAssetImportTargetFileInfo(const FString& InSourceFile, const FString& InTargetFile, bool bInTargetExist)
  3512. : SourceFile(InSourceFile)
  3513. , TargetFile(InTargetFile)
  3514. , bTargeFileWasExist(bInTargetExist)
  3515. {
  3516. if (FExtAssetData* SourceAssetData = FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetByFilePath(*InSourceFile))
  3517. {
  3518. AssetContentDir = SourceAssetData->AssetContentRoot;
  3519. SourceAssetName = SourceAssetData->AssetName;
  3520. if (bTargeFileWasExist)
  3521. {
  3522. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  3523. IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
  3524. TArray<FAssetData> TargetAssetDatas;
  3525. AssetRegistry.GetAssetsByPackageName(*FPackageName::FilenameToLongPackageName(InTargetFile), TargetAssetDatas, /*bIncludeOnlyOnDiskAssets*/false);
  3526. if (TargetAssetDatas.Num() > 0 && TargetAssetDatas[0].IsValid())
  3527. {
  3528. FName& SourceAssetClass = SourceAssetData->AssetClass;
  3529. FName TargetAssetClass = TargetAssetDatas[0].AssetClassPath.GetAssetName();
  3530. bSourceTargetHasSameClass = SourceAssetClass == TargetAssetClass;
  3531. ECB_LOG(Display, TEXT("%s Source Class: %s, Target Class: %s"), *FPaths::GetCleanFilename(InTargetFile), *SourceAssetClass.ToString(), *TargetAssetClass.ToString());
  3532. }
  3533. else
  3534. {
  3535. bSourceTargetHasSameClass = true;
  3536. ECB_LOG(Display, TEXT("TargetAsset %s is empty, set bSourceTargetHasSameClass to true for alwasy overwrite"), *FPaths::GetCleanFilename(InTargetFile));
  3537. }
  3538. }
  3539. }
  3540. }
  3541. };
  3542. struct FExtAssetImportInfo
  3543. {
  3544. // Dependencies
  3545. FExtAssetDependencyInfo DependencyInfo;
  3546. TArray<FExtAssetImportTargetFileInfo> TargetFiles;
  3547. TMap<FString, FExtAssetImportTargetFolderInfo> TargetColoredFolders;
  3548. /**
  3549. * @bFlatMode : convert all target files path in one folder
  3550. */
  3551. void GatherTargetFileInfos(const FExtAssetData& AssetData, const FString& AssetContentDir, const FString& TargetContentRoot, const FUAssetImportSetting& InImportSetting)
  3552. {
  3553. ECB_LOG(Display, TEXT("[GatherTargetFileInfos] for %s"), *AssetData.PackageFilePath.ToString());
  3554. const bool bFlatMode = InImportSetting.bFlatMode;
  3555. const bool bSandboxMode = InImportSetting.bSandboxMode;
  3556. const bool bExportMode = InImportSetting.bExportMode;
  3557. const bool bDirectCopyMode = InImportSetting.bDirectCopyMode;
  3558. const bool bImportFolderColor = !bFlatMode && !bSandboxMode && !bExportMode && InImportSetting.bImportFolderColor && FExtContentBrowserSingleton::GetAssetRegistry().IsAsetContentRootHasFolderColor(AssetContentDir);
  3559. const bool bOverrideFolderColor = InImportSetting.bOverrideExistingFolderColor;
  3560. TArray<FName> ColoredFolders;
  3561. if (bImportFolderColor)
  3562. {
  3563. FExtContentBrowserSingleton::GetAssetRegistry().GetAssetContentRootColoredFolders(AssetContentDir, ColoredFolders);
  3564. }
  3565. DependencyInfo = FExtContentBrowserSingleton::GetAssetRegistry().GetOrCacheAssetDependencyInfo(AssetData);
  3566. const FString GameRootWithSlash(TEXT("/Game/"));
  3567. const FString SandboxRootWithSlash = FExtAssetData::GetSandboxPackagePathWithSlash();
  3568. FString DefaultProjectContentDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
  3569. FPaths::NormalizeDirectoryName(DefaultProjectContentDir);
  3570. TMap<FString, int32> UniqueFlatFileNames;
  3571. const TArray<FString> SourceFileOnly = { AssetData.PackageFilePath.ToString() };
  3572. const TArray<FString>& AllDependentFiles = bDirectCopyMode ? SourceFileOnly : DependencyInfo.ValidDependentFiles;
  3573. for (const FString& ValidDependentFile : AllDependentFiles)
  3574. {
  3575. if (FExtAssetData* DependentAssetPtr = FExtContentBrowserSingleton::GetAssetRegistry().GetOrCacheAssetByFilePath(FName(*ValidDependentFile)))
  3576. {
  3577. FExtAssetData& DependentAsset = *DependentAssetPtr;
  3578. FString OrphanAssetContentRoot = AssetContentDir;
  3579. FString OrphaAssetRelativePath;
  3580. bool bHasContentRootForDirectCopyOrphanAsset = false;
  3581. #if ECB_WIP_IMPORT_ORPHAN
  3582. if (!DependentAsset.HasContentRoot() && DependentAsset.AssetContentType == FExtAssetData::EContentType::Orphan)
  3583. {
  3584. const bool bHasCachedCotentRoot = FExtContentBrowserSingleton::GetAssetRegistry().GetAssetContentRoot(DependentAsset.PackageFilePath.ToString(), OrphanAssetContentRoot);
  3585. bHasContentRootForDirectCopyOrphanAsset = InImportSetting.bDirectCopyMode && DependentAsset.AssetContentType == FExtAssetData::EContentType::Orphan;
  3586. if (bHasCachedCotentRoot || bHasContentRootForDirectCopyOrphanAsset)
  3587. {
  3588. OrphaAssetRelativePath = DependentAsset.PackageFilePath.ToString().RightChop(OrphanAssetContentRoot.Len());
  3589. FPaths::NormalizeFilename(OrphaAssetRelativePath);
  3590. if (OrphaAssetRelativePath.StartsWith(TEXT("/")))
  3591. {
  3592. OrphaAssetRelativePath = OrphaAssetRelativePath.Mid(1);
  3593. }
  3594. // Update Asset Content Root for dependency assets use cached info
  3595. if (bHasCachedCotentRoot)
  3596. {
  3597. DependentAsset.AssetContentRoot = *OrphanAssetContentRoot;
  3598. DependentAsset.AssetRelativePath = *OrphaAssetRelativePath;
  3599. }
  3600. }
  3601. }
  3602. #endif
  3603. #if ECB_WIP_IMPORT_ORPHAN
  3604. bool bHasCotentRoot = DependentAsset.HasContentRoot() || bHasContentRootForDirectCopyOrphanAsset;
  3605. #else
  3606. bool bHasCotentRoot = DependentAsset.HasContentRoot();
  3607. #endif
  3608. if (bHasCotentRoot)
  3609. {
  3610. FString AssetContentRoot = bHasContentRootForDirectCopyOrphanAsset ? OrphanAssetContentRoot : DependentAsset.AssetContentRoot.ToString();
  3611. FString RelativePath = bHasContentRootForDirectCopyOrphanAsset ? OrphaAssetRelativePath : DependentAsset.AssetRelativePath.ToString();
  3612. FString FileName = FPaths::GetCleanFilename(RelativePath);
  3613. FString TargetFilePath = FPaths::Combine(TargetContentRoot, RelativePath);
  3614. FPaths::NormalizeFilename(TargetFilePath);
  3615. const bool bTargetFileExist = IFileManager::Get().FileExists(*TargetFilePath);
  3616. ECB_LOG(Display, TEXT(" %s Found and valid! =>(exist? %d)"), *TargetFilePath, bTargetFileExist);
  3617. const FString& SourceFilePath = ValidDependentFile;
  3618. int32 NewIndex = TargetFiles.Emplace(SourceFilePath, TargetFilePath, bTargetFileExist);
  3619. auto& AssetContentType = DependentAsset.AssetContentType;
  3620. const bool bPluginAsset = AssetContentType == FExtAssetData::EContentType::Plugin && !AssetContentDir.IsEmpty();
  3621. FString PluginRootWithSlash = bPluginAsset
  3622. ? FExtAssetDataUtil::GetPluginNameFromAssetContentRoot(AssetContentRoot) // /PluginName/
  3623. : TEXT("");
  3624. if (bPluginAsset)
  3625. {
  3626. FExtAssetImportTargetFileInfo& TargetFile = TargetFiles[NewIndex];
  3627. TargetFile.bPluginAsset = true;
  3628. TargetFile.PluginRootWithSlash = PluginRootWithSlash;
  3629. }
  3630. if (bExportMode)
  3631. {
  3632. continue; // Skip redirect data
  3633. }
  3634. // Gather redirect info
  3635. // if (bPluginAsset || bFlatMode || bSandboxMode)
  3636. {
  3637. FString SourcePackgeName = FPackageName::FilenameToLongPackageName(FPaths::Combine(DefaultProjectContentDir, RelativePath));
  3638. FString TargetPackageName = FPackageName::FilenameToLongPackageName(TargetFilePath);
  3639. // default redirect
  3640. bool bRedirectMe = false;
  3641. FString RedirectFromPackageName = SourcePackgeName;
  3642. FString RedirectToPackageName = TargetPackageName;
  3643. if (bPluginAsset)
  3644. {
  3645. bRedirectMe = true;
  3646. FString ImportingRoot = bSandboxMode ? SandboxRootWithSlash : GameRootWithSlash;
  3647. RedirectFromPackageName = PluginRootWithSlash + SourcePackgeName.Mid(ImportingRoot.Len());
  3648. }
  3649. if (bFlatMode)
  3650. {
  3651. if (UniqueFlatFileNames.Contains(FileName))
  3652. {
  3653. int32 LastIndex = UniqueFlatFileNames[FileName];
  3654. UniqueFlatFileNames[FileName] = LastIndex + 1;
  3655. FileName = FPaths::GetBaseFilename(FileName) + FString::FromInt(LastIndex) + FPaths::GetExtension(FileName, /*bIncludeDot*/ true);
  3656. }
  3657. else
  3658. {
  3659. UniqueFlatFileNames.Add(FileName, 1);
  3660. }
  3661. TargetFilePath = FPaths::Combine(TargetContentRoot, FileName);
  3662. FPaths::NormalizeFilename(TargetFilePath);
  3663. bRedirectMe = true;
  3664. RedirectToPackageName = FPackageName::FilenameToLongPackageName(TargetFilePath);
  3665. }
  3666. if (!bPluginAsset && bSandboxMode && TargetPackageName.StartsWith(SandboxRootWithSlash))
  3667. {
  3668. FString OrigPackageName = GameRootWithSlash/*TEXT("/Game/")*/ + TargetPackageName.Mid(SandboxRootWithSlash.Len());
  3669. bRedirectMe = true;
  3670. RedirectFromPackageName = OrigPackageName;
  3671. }
  3672. if (!bRedirectMe)
  3673. {
  3674. FString SourceRootPackage = FExtAssetDataUtil::GetPackageRootFromFullPackagePath(SourcePackgeName);
  3675. FString TargetRootPackage = FExtAssetDataUtil::GetPackageRootFromFullPackagePath(TargetPackageName);
  3676. if (!SourceRootPackage.Equals(TargetRootPackage))
  3677. {
  3678. bRedirectMe = true;
  3679. }
  3680. }
  3681. if (bRedirectMe)
  3682. {
  3683. FExtAssetImportTargetFileInfo& TargetFile = TargetFiles[NewIndex];
  3684. TargetFile.bRedirectMe = true;
  3685. TargetFile.RedirectFromPackageName = RedirectFromPackageName;
  3686. TargetFile.RedirectToPackageName = RedirectToPackageName;
  3687. TargetFile.TargetFile = TargetFilePath;
  3688. }
  3689. // Gather Folder Colors
  3690. #if ECB_WIP_IMPORT_FOLDER_COLOR
  3691. if (bImportFolderColor && ColoredFolders.Num() > 0)
  3692. {
  3693. FExtAssetImportTargetFileInfo& TargetFile = TargetFiles[NewIndex];
  3694. for (int32 Index = ColoredFolders.Num() - 1; Index >= 0; --Index)
  3695. {
  3696. auto& ColoredFolder = ColoredFolders[Index];
  3697. FString ColoredFolderStr = ColoredFolder.ToString();
  3698. if (SourceFilePath.StartsWith(ColoredFolderStr))
  3699. {
  3700. FString FolderRelativePath = ColoredFolderStr.Mid(FCString::Strlen(*AssetContentDir) + 1);
  3701. FString TargetPackageRoot = bRedirectMe
  3702. ? FExtAssetDataUtil::GetPackageRootFromFullPackagePath(RedirectToPackageName)
  3703. : FExtAssetDataUtil::GetPackageRootFromFullPackagePath(TargetPackageName);
  3704. FString TargetFolderPackagePath = TargetPackageRoot + FolderRelativePath;
  3705. FLinearColor FolderColor;
  3706. if (!FExtContentBrowserSingleton::GetAssetRegistry().GetFolderColor(ColoredFolderStr, FolderColor))
  3707. {
  3708. continue;
  3709. }
  3710. if (!TargetColoredFolders.Contains(TargetFolderPackagePath))
  3711. {
  3712. FExtAssetImportTargetFolderInfo TargetFolderInfo(TargetFolderPackagePath, FolderColor);
  3713. if (!TargetFolderInfo.bTargetFolderColorExist || (TargetFolderInfo.bTargetFolderColorExist && bOverrideFolderColor))
  3714. {
  3715. TargetColoredFolders.Add(TargetFolderPackagePath, TargetFolderInfo);
  3716. }
  3717. }
  3718. ECB_LOG(Display, TEXT(" TargetFolderPackagePath: %s, Color: %s"), *TargetFolderPackagePath, *FolderColor.ToString());
  3719. ColoredFolders.RemoveAt(Index);
  3720. }
  3721. }
  3722. }
  3723. #endif
  3724. }
  3725. }
  3726. }
  3727. }
  3728. }
  3729. };
  3730. struct FExtAssetRollbackInfo
  3731. {
  3732. void MapSourceToTarget(const FName& InSource, const FName& InTarget)
  3733. {
  3734. SourceToTargetMap.Add(InSource, InTarget);
  3735. }
  3736. void Backup(const FName& InTarget, const FName& InBackup)
  3737. {
  3738. TargetToBackupMap.Add(InTarget, InBackup);
  3739. }
  3740. void MapNewFile(const FName& InSource, const FName& InNewFile, const FName& InNewCreatedDir)
  3741. {
  3742. SourceToNewFileMap.Add(InSource, InNewFile);
  3743. CreatedDirs.Add(InNewCreatedDir);
  3744. }
  3745. void Backup(const FExtAssetImportTargetFolderInfo& InFolderInfoToBackup)
  3746. {
  3747. if (InFolderInfoToBackup.bTargetFolderColorExist)
  3748. {
  3749. OverrideColoredFolders.Add(InFolderInfoToBackup);
  3750. }
  3751. else
  3752. {
  3753. NewColoredFolders.Add(InFolderInfoToBackup);
  3754. }
  3755. }
  3756. void RollbackAll()
  3757. {
  3758. ECB_LOG(Display, TEXT("[Rollback] %d backup, %d new."), TargetToBackupMap.Num(), SourceToNewFileMap.Num());
  3759. // Delete new created files
  3760. for (auto& FilePair : SourceToNewFileMap)
  3761. {
  3762. const FName& NewFile = FilePair.Value;
  3763. if (IFileManager::Get().FileExists(*NewFile.ToString()))
  3764. {
  3765. const bool bRequireExists = false;
  3766. const bool bEvenReadOnly = false;
  3767. const bool bQuiet = false;
  3768. bool bReuslt = IFileManager::Get().Delete(*NewFile.ToString(), bRequireExists, bEvenReadOnly, bQuiet);
  3769. if (bReuslt)
  3770. {
  3771. ECB_LOG(Display, TEXT(" X Success to delete new file:\n\t\t%s."), *NewFile.ToString());
  3772. }
  3773. else
  3774. {
  3775. ECB_LOG(Error, TEXT(" X Failed to delete new file:\n\t\t%s."), *NewFile.ToString());
  3776. }
  3777. }
  3778. else
  3779. {
  3780. ECB_LOG(Warning, TEXT(" X Can't find new file:\n\t\t%s."), *NewFile.ToString());
  3781. }
  3782. }
  3783. // Move back backup files
  3784. TArray<UPackage*> ReplacedPackages;
  3785. for (auto It = TargetToBackupMap.CreateIterator(); It; ++It)
  3786. {
  3787. const FName& Target = It->Key;
  3788. const FName& Backup = It->Value;
  3789. if (IFileManager::Get().FileExists(*Backup.ToString()))
  3790. {
  3791. FExtPackageUtils::UnloadPackage({ Target.ToString() }, &ReplacedPackages);
  3792. const bool bReplace = true;
  3793. const bool bEvenIfReadOnly = true;
  3794. const bool bAttributes = true;
  3795. const bool bDoNotRetryOrError = false;
  3796. bool bResult = IFileManager::Get().Move(/*Dest=*/*Target.ToString(), /*Src=*/ *Backup.ToString(), bReplace, bEvenIfReadOnly, bAttributes, bDoNotRetryOrError);
  3797. if (bResult)
  3798. {
  3799. ECB_LOG(Display, TEXT(" <- Success Move backuped file from \n\t\t%s \n\t\tto %s."), *Backup.ToString(), *Target.ToString());
  3800. }
  3801. else
  3802. {
  3803. ECB_LOG(Error, TEXT(" <- Failed to move backuped file from \n\t\t%s \n\t\tto %s."), *Backup.ToString(), *Target.ToString());
  3804. }
  3805. }
  3806. else
  3807. {
  3808. ECB_LOG(Warning, TEXT(" X Can't find backup file:\n\t\t%s."), *Backup.ToString());
  3809. }
  3810. }
  3811. // Delete new created dirs
  3812. TArray<FString> NewCreateDirs;
  3813. for (auto& Dir : CreatedDirs)
  3814. {
  3815. NewCreateDirs.Emplace(Dir.ToString());
  3816. }
  3817. TArray<FString> MergedCreateDirs;
  3818. FPathsUtil::SortAndMergeDirs(NewCreateDirs, MergedCreateDirs);
  3819. for (auto& CreatedDir : MergedCreateDirs)
  3820. {
  3821. if (IFileManager::Get().DirectoryExists(*CreatedDir))
  3822. {
  3823. bool bReuslt = IFileManager::Get().DeleteDirectory(*CreatedDir, /*RequireExists */ false, /*Tree */ true);
  3824. if (bReuslt)
  3825. {
  3826. ECB_LOG(Display, TEXT(" X Success delete new dir:\n\t\t%s."), *CreatedDir);
  3827. }
  3828. else
  3829. {
  3830. ECB_LOG(Error, TEXT(" X Failed to delete new dir:\n\t\t%s."), *CreatedDir);
  3831. }
  3832. }
  3833. }
  3834. // Rollback folder colors
  3835. {
  3836. for (auto& FolderInfo : OverrideColoredFolders)
  3837. {
  3838. AssetViewUtils::SetPathColor(FolderInfo.TargetFolderPacakgeName, TOptional<FLinearColor>()); // clear PatchColors cache
  3839. AssetViewUtils::SetPathColor(FolderInfo.TargetFolderPacakgeName, TOptional<FLinearColor>(FLinearColor(FolderInfo.ExistingTargetFolderColor)));
  3840. }
  3841. for (auto& FolderInfo : NewColoredFolders)
  3842. {
  3843. AssetViewUtils::SetPathColor(FolderInfo.TargetFolderPacakgeName, TOptional<FLinearColor>());
  3844. }
  3845. }
  3846. UPackageTools::ReloadPackages(ReplacedPackages);
  3847. }
  3848. #if ECB_TODO // todo: should support rollback individual file?
  3849. void RollbackImportedFile(const FName& InTargetFile)
  3850. {
  3851. // Delete new file if found
  3852. if (FName* NewFile = SourceToNewFileMap.Find(InTargetFile))
  3853. {
  3854. }
  3855. // Or restore backup
  3856. else
  3857. {
  3858. if (FName* Target = SourceToTargetMap.Find(InTargetFile))
  3859. {
  3860. if (FName* Backup = TargetToBackupMap.Find(*Target))
  3861. {
  3862. }
  3863. }
  3864. }
  3865. }
  3866. #endif
  3867. void DeleteBackupFiles()
  3868. {
  3869. // Delete all backup files
  3870. for (auto& FilePair : TargetToBackupMap)
  3871. {
  3872. const FName& Backup = FilePair.Value;
  3873. if (IFileManager::Get().FileExists(*Backup.ToString()))
  3874. {
  3875. const bool bRequireExists = false;
  3876. const bool bEvenReadOnly = false;
  3877. const bool bQuiet = false;
  3878. bool bReuslt = IFileManager::Get().Delete(*Backup.ToString(), bRequireExists, bEvenReadOnly, bQuiet);
  3879. ECB_LOG(Display, TEXT(" X Delete backup:\n\t\t%s.\n\t\t success? %d"), *Backup.ToString(), bReuslt);
  3880. }
  3881. else
  3882. {
  3883. ECB_LOG(Display, TEXT(" * Backup file already deleted:\n\t\t%s."), *Backup.ToString());
  3884. }
  3885. }
  3886. }
  3887. TMap<FName, FName> SourceToTargetMap;
  3888. TMap<FName, FName> TargetToBackupMap;
  3889. TMap<FName, FName> SourceToNewFileMap;
  3890. TSet<FName> CreatedDirs;
  3891. TArray<FExtAssetImportTargetFolderInfo> OverrideColoredFolders;
  3892. TArray<FExtAssetImportTargetFolderInfo> NewColoredFolders;
  3893. };
  3894. struct FExtAssetImportTask
  3895. {
  3896. bool IfFoundInvalidOrMissingDependency() const
  3897. {
  3898. return ImportInfo.DependencyInfo.InvalidDependentFiles.Num() > 0
  3899. || ImportInfo.DependencyInfo.MissingDependentFiles.Num() > 0
  3900. || ImportInfo.DependencyInfo.MissingPackageNames.Num() > 0;
  3901. }
  3902. bool IfAllTargetFilesWereExist() const
  3903. {
  3904. bool bAllExist = true;
  3905. for (const FExtAssetImportTargetFileInfo& Info : ImportInfo.TargetFiles)
  3906. {
  3907. if (!Info.bTargeFileWasExist)
  3908. {
  3909. bAllExist = false;
  3910. break;
  3911. }
  3912. }
  3913. return bAllExist;
  3914. }
  3915. bool IsSuccessImported() const
  3916. {
  3917. return Status == EImportTaskStatus::Imported;
  3918. }
  3919. bool IsSkipped() const
  3920. {
  3921. return Status == EImportTaskStatus::Skipped;
  3922. }
  3923. bool IsNotStarted() const
  3924. {
  3925. return Status == EImportTaskStatus::NotStarted;
  3926. }
  3927. bool IsRollbacked() const
  3928. {
  3929. return Status == EImportTaskStatus::Rollbacked;
  3930. }
  3931. void Rollback()
  3932. {
  3933. RollbackInfo.RollbackAll();
  3934. Status = EImportTaskStatus::Rollbacked;
  3935. }
  3936. bool IsDependsOn(const FString& InDependencyFilePath)
  3937. {
  3938. return ImportInfo.DependencyInfo.ValidDependentFiles.Find(InDependencyFilePath) != INDEX_NONE;
  3939. }
  3940. public:
  3941. // Source
  3942. FExtAssetData SourceAssetData;
  3943. FString GetMainSourceFilePath() { return SourceAssetData.PackageFilePath.ToString(); }
  3944. // ImportInfo
  3945. FExtAssetImportInfo ImportInfo;
  3946. // Rollback
  3947. FExtAssetRollbackInfo RollbackInfo;
  3948. // Result
  3949. FAssetData ImportedMainAssetData;
  3950. enum class EImportTaskStatus : uint8
  3951. {
  3952. NotStarted,
  3953. Skipped,
  3954. Imported,
  3955. Failed,
  3956. Rollbacked,
  3957. };
  3958. EImportTaskStatus Status = EImportTaskStatus::NotStarted;
  3959. };
  3960. struct FExtAssetImportSession
  3961. {
  3962. FExtAssetImportSession(const FUAssetImportSetting& InImportSetting)
  3963. {
  3964. ImportSetting = InImportSetting;
  3965. FPaths::NormalizeDirectoryName(ImportSetting.TargetContentDir); // double make sure no ending slash
  3966. const FString SessionId = FGuid::NewGuid().ToString(EGuidFormats::Digits);
  3967. SesstionTempDir = FPaths::Combine(FExtAssetData::GetImportSessionTempDir(), SessionId);
  3968. if (!IFileManager::Get().DirectoryExists(*SesstionTempDir))
  3969. {
  3970. if (!IFileManager::Get().MakeDirectory(*SesstionTempDir, /*tree*/ true))
  3971. {
  3972. ECB_LOG(Display, TEXT("[FExtAssetImportSession] Failed to create backup dir: %s"), *SesstionTempDir);
  3973. }
  3974. }
  3975. }
  3976. ~FExtAssetImportSession()
  3977. {
  3978. if (IFileManager::Get().DirectoryExists(*SesstionTempDir))
  3979. {
  3980. const bool bRequireExists = false;
  3981. const bool bTree = true;
  3982. if (!IFileManager::Get().DeleteDirectory(*SesstionTempDir, bRequireExists, bTree))
  3983. {
  3984. ECB_LOG(Display, TEXT("[FExtAssetImportSession] Failed to delete backup dir: %s"), *SesstionTempDir);
  3985. }
  3986. }
  3987. }
  3988. void ImportAssets(const TArray<FExtAssetData>& InAssetDatas)
  3989. {
  3990. int32 NumMainAssetsToImport = InAssetDatas.Num();
  3991. ECB_LOG(Display, TEXT("========== Importing %d Assets ============"), NumMainAssetsToImport);
  3992. if (NumMainAssetsToImport <= 0)
  3993. {
  3994. return;
  3995. }
  3996. ECB_LOG(Display, TEXT("------------ Phase 1: Gathering dependencies -----------------"));
  3997. {
  3998. PrepareImportTasksWithDependencies(InAssetDatas);
  3999. if (IsSessionRequestToCancel())
  4000. {
  4001. const FString FirstAssetFilePath = InAssetDatas[0].PackageFilePath.ToString();
  4002. FString AbortMessage = FString::Printf(TEXT("Import %s%s aborted!")
  4003. , *FPaths::GetBaseFilename(FirstAssetFilePath)
  4004. , (NumMainAssetsToImport ? TEXT("") : *FString::Printf(TEXT(" and other %d assets"), NumMainAssetsToImport - 1)));
  4005. ExtContentBrowserUtils::NotifyMessage(FText::FromString(AbortMessage));
  4006. return;
  4007. }
  4008. const int32 NumAllAsssetsToImport = GetNumAllAssetsToImport();
  4009. if (NumAllAsssetsToImport < 1)
  4010. {
  4011. FString AbortMessage = TEXT("No asset files been gathered to import, aborting!");
  4012. ExtContentBrowserUtils::NotifyMessage(FText::FromString(AbortMessage));
  4013. return;
  4014. }
  4015. }
  4016. ECB_LOG(Display, TEXT("------------ Phase 2: Importing asset files -----------------"));
  4017. {
  4018. const int32 NumAllAsssetsToImport = GetNumAllAssetsToImport();
  4019. const float TotalSteps = NumAllAsssetsToImport + 1;
  4020. TSharedPtr<FExtAssetCoreUtil::FExtAssetFeedbackContext> FeedbackContext(new FExtAssetCoreUtil::FExtAssetFeedbackContext);
  4021. FScopedSlowTask SlowTask(TotalSteps, FText::Format(LOCTEXT("ImportingNAssets", "Importing {0} Assets..."), FText::AsNumber(NumAllAsssetsToImport))
  4022. , /*bEnabled*/ true,
  4023. *FeedbackContext.Get()
  4024. );
  4025. SlowTask.MakeDialog(/*bShowCancelDialog =*/ true);
  4026. RunImportTasks(SlowTask);
  4027. }
  4028. TArray<FAssetData> SuccessImportedAssets;
  4029. TArray<FAssetData> SuccessImportedMainAssets;
  4030. ECB_LOG(Display, TEXT("------------ Phase 3: Validate and Sync/Redirect -----------------"));
  4031. if (!IsSessionRequestToCancel())
  4032. {
  4033. int32 NumFilesToValidateOrSync = GetImportedFilePaths(/*bIncludeSkipped*/ImportSetting.bSyncExistingAssets).Num();
  4034. TSharedPtr<FExtAssetCoreUtil::FExtAssetFeedbackContext> FeedbackContext(new FExtAssetCoreUtil::FExtAssetFeedbackContext);
  4035. const float TotalSteps = RedirectInfo.NoRedirectImportedPackageNames.Num() + 1;
  4036. FScopedSlowTask SlowTask(TotalSteps, FText::Format(LOCTEXT("ValidatingNAssets", "Validating {0} Assets..."), FText::AsNumber(NumFilesToValidateOrSync))
  4037. , /*bEnabled*/ true,
  4038. *FeedbackContext.Get()
  4039. );
  4040. SlowTask.MakeDialog(/*bShowCancelDialog =*/ true);
  4041. //SlowTask.EnterProgressFrame(1, LOCTEXT("ImportingAssets_Validate", "Validating imported assets..."));
  4042. ValidateSyncRedirectImportResult(SlowTask, SuccessImportedAssets, SuccessImportedMainAssets);
  4043. }
  4044. ECB_LOG(Display, TEXT("------------ Phase 4: Rollback and cleanup -----------------"));
  4045. {
  4046. FScopedSlowTask SlowTask(1, LOCTEXT("ImportingAssets_PostImport", "Finish Importing..."));
  4047. SlowTask.MakeDialog(/*bShowCancelDialog =*/ false);
  4048. if (!IsSessionRequestToCancel())
  4049. {
  4050. SlowTask.EnterProgressFrame(.5f, LOCTEXT("ImportingAssets_Finishing", "Finishing import..."));
  4051. }
  4052. else
  4053. {
  4054. SlowTask.EnterProgressFrame(.5f, LOCTEXT("ImportingngAssets_Canceling", "Canceling import..."));
  4055. }
  4056. PostImport(SlowTask);
  4057. }
  4058. ECB_LOG(Display, TEXT("------------ Phase 5: Load assets -----------------"));
  4059. if (const bool bShouldLoadAssets = ImportSetting.bLoadAssetAfterImport && !IsSessionRequestToCancel() && !IsSessionRollbacked())
  4060. {
  4061. const float TotalSteps = NumMainAssetsToImport + 1;
  4062. FScopedSlowTask SlowTask(TotalSteps, FText::Format(LOCTEXT("LoadingNAssets", "Loading {0} Assets..."), FText::AsNumber(NumMainAssetsToImport)));
  4063. SlowTask.MakeDialog(/*bShowCancelDialog =*/ false);
  4064. SlowTask.EnterProgressFrame(0, LOCTEXT("ImportingAssets_LoadAssets", "Loading assets..."));
  4065. LoadAssets(SlowTask, SuccessImportedAssets);
  4066. // Reload redirected packages ?
  4067. const bool bReloadRedirectedPackages = false;
  4068. if (bReloadRedirectedPackages)
  4069. {
  4070. // Don't allow asset reload during PIE
  4071. if (GIsEditor)
  4072. {
  4073. if (SuccessImportedAssets.Num() > 0)
  4074. {
  4075. TArray<UPackage*> PackagesToReload;
  4076. for (const FAssetData& AssetData : SuccessImportedAssets)
  4077. {
  4078. if (AssetData.AssetClassPath.GetPackageName() == UObjectRedirector::StaticClass()->GetFName())
  4079. {
  4080. // Don't operate on Redirectors
  4081. continue;
  4082. }
  4083. if (AssetData.AssetClassPath.GetPackageName() == UUserDefinedStruct::StaticClass()->GetFName())
  4084. {
  4085. // User created structures cannot be safely reloaded.
  4086. continue;
  4087. }
  4088. if (AssetData.AssetClassPath.GetPackageName() == UUserDefinedEnum::StaticClass()->GetFName())
  4089. {
  4090. // User created enumerations cannot be safely reloaded.
  4091. continue;
  4092. }
  4093. UPackage* Package = AssetData.GetPackage();
  4094. if (Package->IsFullyLoaded() && IsPackageRedirected(AssetData.PackageName.ToString()))
  4095. {
  4096. PackagesToReload.AddUnique(Package);
  4097. }
  4098. }
  4099. if (PackagesToReload.Num() > 0)
  4100. {
  4101. UPackageTools::ReloadPackages(PackagesToReload);
  4102. }
  4103. }
  4104. }
  4105. }
  4106. }
  4107. #if ECB_WIP_IMPORT_ADD_TO_COLLECTION
  4108. ECB_LOG(Display, TEXT("------------ Phase 6: Add to Collection -----------------"));
  4109. if (!IsSessionRequestToCancel() && !IsSessionRollbacked())
  4110. {
  4111. AddImportedAssetsToCollection(SuccessImportedAssets);
  4112. }
  4113. #endif
  4114. #if ECB_FEA_IMPORT_ADD_PLACE
  4115. ECB_LOG(Display, TEXT("------------ Phase 7: Place imported assets -----------------"));
  4116. if (ImportSetting.bPlaceImportedAssets)
  4117. {
  4118. if (!IsSessionRequestToCancel() && !IsSessionRollbacked()
  4119. && SuccessImportedMainAssets.Num() > 0 && GCurrentLevelEditingViewportClient)
  4120. {
  4121. PlaceImportedMainAssetsToLevelEditor(SuccessImportedMainAssets);
  4122. }
  4123. }
  4124. #endif
  4125. #if ECB_WIP_IMPORT_FOR_DUMP
  4126. ECB_LOG(Display, TEXT("------------ Phase 8: Dump imported assets -----------------"));
  4127. if (ImportSetting.bDumpAsset)
  4128. {
  4129. if (!IsSessionRequestToCancel() && !IsSessionRollbacked()
  4130. && SuccessImportedMainAssets.Num() > 0)
  4131. {
  4132. DumpImportedMainAssetsViaExportDialog(SuccessImportedMainAssets);
  4133. }
  4134. ECB_LOG(Display, TEXT("------------ Phase 8.1: Cleanup -----------------"));
  4135. if (!IsSessionRollbacked())
  4136. {
  4137. FScopedSlowTask SlowTask(1, LOCTEXT("ImportingAssets_Cleanup", "Cleanup..."));
  4138. SlowTask.MakeDialog(/*bShowCancelDialog =*/ false);
  4139. RollbackSession(SlowTask);
  4140. Cleanup();
  4141. }
  4142. }
  4143. #endif
  4144. }
  4145. void PrepareImportTasksWithDependencies(const TArray<FExtAssetData>& InAssetDatas)
  4146. {
  4147. const int32 NumMainAssetsToImport = InAssetDatas.Num();
  4148. ECB_LOG(Display, TEXT("[PrepareImportTasksWithDependencies] for %d assets."), NumMainAssetsToImport);
  4149. if (NumMainAssetsToImport <= 0)
  4150. {
  4151. return;
  4152. }
  4153. const bool bNoDepeendencyGathering = ImportSetting.bDirectCopyMode;
  4154. TSet<FName> DependencyGatheredFiles; // All files already analyzed to gather dependencies
  4155. for (int32 Index = 0; Index < NumMainAssetsToImport; ++Index)
  4156. {
  4157. const FExtAssetData& AssetData = InAssetDatas[Index];
  4158. FName PackageFilePath = AssetData.PackageFilePath;
  4159. ECB_LOG(Display, TEXT(" %d) %s"), Index, *PackageFilePath.ToString());
  4160. if (DependencyGatheredFiles.Contains(PackageFilePath))
  4161. {
  4162. ECB_LOG(Display, TEXT(" => Already analyzed. Skip analyzing %s."), *PackageFilePath.ToString());
  4163. break;
  4164. }
  4165. DependencyGatheredFiles.Add(PackageFilePath);
  4166. const bool bAssetFileExist = IFileManager::Get().FileExists(*PackageFilePath.ToString());
  4167. if (!bAssetFileExist)
  4168. {
  4169. ECB_LOG(Display, TEXT(" => Asset file: %s not exist. Skip."), *PackageFilePath.ToString());
  4170. break;
  4171. }
  4172. if (!bNoDepeendencyGathering)
  4173. {
  4174. FExtAssetDependencyInfo DependencyInfo = FExtContentBrowserSingleton::GetAssetRegistry().GetOrCacheAssetDependencyInfo(AssetData, /*bShowProgess*/ true);
  4175. if (DependencyInfo.AssetStatus == EDependencyNodeStatus::AbortGathering)
  4176. {
  4177. bSessionRequestToCancel = true;
  4178. ECB_LOG(Error, TEXT("[PrepareImportTasksWithDependencies] GetOrCacheAssetDependencyInfo aborted: "), *FPaths::GetBaseFilename(PackageFilePath.ToString()));
  4179. break;
  4180. }
  4181. if (DependencyInfo.AssetStatus == EDependencyNodeStatus::ValidWithSoftReferenceIssue && ImportSetting.bIgnoreSoftReferencesError)
  4182. {
  4183. ImportSetting.bSkipImportIfAnyDependencyMissing = false;
  4184. }
  4185. else if (DependencyInfo.AssetStatus != EDependencyNodeStatus::Valid && ImportSetting.bSkipImportIfAnyDependencyMissing)
  4186. {
  4187. ECB_LOG(Display, TEXT(" => Asset file: %s not valid. Skip."), *PackageFilePath.ToString());
  4188. break;
  4189. }
  4190. }
  4191. FExtAssetImportTask NewTask;
  4192. NewTask.SourceAssetData = AssetData;
  4193. if (AssetData.HasContentRoot())
  4194. {
  4195. FString AssetFileRelativePath = AssetData.AssetRelativePath.ToString();
  4196. FString AssetContentDir = AssetData.AssetContentRoot.ToString();
  4197. ECB_LOG(Display, TEXT(" => %s relative to %s."), *AssetFileRelativePath, *AssetContentDir);
  4198. if (FPaths::IsSamePath(AssetContentDir, ImportSetting.TargetContentDir))
  4199. {
  4200. ECB_LOG(Display, TEXT(" Asset Cotent Dir (%s) same as project content dir (%s), skip importing %s."), *AssetContentDir, *ImportSetting.TargetContentDir, *AssetFileRelativePath);
  4201. FString SkipMessage = FString::Printf(TEXT("Can't import assets into same project: %s."), *AssetContentDir);
  4202. SetTaskSkipped(NewTask, SkipMessage);
  4203. continue;
  4204. }
  4205. FExtAssetImportInfo& ImportInfo = NewTask.ImportInfo;
  4206. ImportInfo.GatherTargetFileInfos(AssetData, AssetContentDir, ImportSetting.TargetContentDir, ImportSetting);
  4207. }
  4208. else
  4209. {
  4210. #if ECB_WIP_IMPORT_ORPHAN
  4211. FString RootContentPath;
  4212. if (/*ImportSetting.bDirectCopyMode && */AssetData.AssetContentType == FExtAssetData::EContentType::Orphan
  4213. && FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPathFromFilePath(AssetData.PackageFilePath.ToString(), RootContentPath)
  4214. )
  4215. {
  4216. FExtAssetImportInfo& ImportInfo = NewTask.ImportInfo;
  4217. ImportInfo.GatherTargetFileInfos(AssetData, RootContentPath, ImportSetting.TargetContentDir, ImportSetting);
  4218. }
  4219. else
  4220. #endif
  4221. {
  4222. ECB_LOG(Display, TEXT(" => Can't find relative path to asset content root, skip importing %s."), *FPaths::GetCleanFilename(PackageFilePath.ToString()));
  4223. FString SkipMessage = FString::Printf(TEXT("Can't find relative path to asset content root for %s."), *FPaths::GetCleanFilename(PackageFilePath.ToString()));
  4224. SetTaskSkipped(NewTask, SkipMessage);
  4225. }
  4226. }
  4227. ImportTasks.Add(NewTask);
  4228. }
  4229. }
  4230. void RunImportTasks(FScopedSlowTask& SlowTask)
  4231. {
  4232. if (SlowTask.ShouldCancel())
  4233. {
  4234. ECB_LOG(Warning, TEXT("[RunImportTasks] Canceled"));
  4235. return;
  4236. }
  4237. int32 NumImportTasks = ImportTasks.Num();
  4238. ECB_LOG(Display, TEXT("[RunImportTasks] %d tasks."), NumImportTasks);
  4239. if (NumImportTasks <= 0)
  4240. {
  4241. return;
  4242. }
  4243. // All imported files including dependency file path to result map
  4244. TMap<FName, bool> ImportedFilesResult;
  4245. #if ECB_WIP_IMPORT_FOLDER_COLOR
  4246. // All imported colored folders
  4247. TSet<FString> ImportedColoredFolders;
  4248. #endif
  4249. bool bCancelRequestedByUser = false;
  4250. for (auto& Task : ImportTasks)
  4251. {
  4252. if (!Task.IsNotStarted())
  4253. {
  4254. continue;
  4255. }
  4256. if (Task.IfFoundInvalidOrMissingDependency())
  4257. {
  4258. ECB_INFO(Warning, TEXT("Dependency missing or invaid when importing %s. (Invalid files: %d, Missing files: %d, Missing Packages: %d"), *Task.GetMainSourceFilePath()
  4259. , Task.ImportInfo.DependencyInfo.InvalidDependentFiles.Num()
  4260. , Task.ImportInfo.DependencyInfo.MissingDependentFiles.Num()
  4261. , Task.ImportInfo.DependencyInfo.MissingPackageNames.Num());
  4262. if (ImportSetting.bSkipImportIfAnyDependencyMissing)
  4263. {
  4264. ECB_LOG(Display, TEXT(" => Skip according to import setting."));
  4265. const int32 NumInvalid = Task.ImportInfo.DependencyInfo.InvalidDependentFiles.Num();
  4266. const int32 NumMissing = Task.ImportInfo.DependencyInfo.MissingDependentFiles.Num() + Task.ImportInfo.DependencyInfo.MissingPackageNames.Num();
  4267. FString InvalidMessage = NumInvalid > 0 ? FString::Printf(TEXT("%d %s invalid. "), NumInvalid, (NumInvalid == 1 ? TEXT("dependency") : TEXT("dependencies"))) : TEXT("");
  4268. FString MissingMessage = NumMissing > 0 ? FString::Printf(TEXT("%d %s missing."), NumMissing, (NumMissing == 1 ? TEXT("dependency") : TEXT("dependencies"))) : TEXT("");
  4269. FString SkipMessage = FString::Printf(TEXT("%s%s"), *InvalidMessage, *MissingMessage);
  4270. SetTaskSkipped(Task, SkipMessage);
  4271. continue;
  4272. }
  4273. }
  4274. if (Task.IfAllTargetFilesWereExist())
  4275. {
  4276. ECB_INFO(Warning, TEXT("All files including %d dependencies were exist when importing %s."), Task.ImportInfo.DependencyInfo.ValidDependentFiles.Num(), *Task.GetMainSourceFilePath());
  4277. if (!ImportSetting.bOverwriteExistingFiles)
  4278. {
  4279. ECB_LOG(Display, TEXT(" => Skip according to import setting."));
  4280. FString SkipMessage = FString::Printf(TEXT("All files including %d dependencies were exist."), Task.ImportInfo.DependencyInfo.ValidDependentFiles.Num());
  4281. SetTaskSkipped(Task, SkipMessage);
  4282. FString MainSourceFile = Task.GetMainSourceFilePath();
  4283. for (FExtAssetImportTargetFileInfo TargetFileInfo : Task.ImportInfo.TargetFiles)
  4284. {
  4285. if (MainSourceFile.Equals(TargetFileInfo.SourceFile))
  4286. {
  4287. TargetFileInfo.bMainAsset = true;
  4288. }
  4289. TargetFileInfo.bSkipped = true;
  4290. ImportedFilesInSession.Add(TargetFileInfo);
  4291. }
  4292. //continue;
  4293. }
  4294. }
  4295. if (!Task.IsSkipped())
  4296. {
  4297. ECB_LOG(Display, TEXT("Importing %s with %d dependencies will be imported)"), *Task.GetMainSourceFilePath(), Task.ImportInfo.DependencyInfo.ValidDependentFiles.Num());
  4298. bool bFailed = false;
  4299. FString FailMessage;
  4300. for (FExtAssetImportTargetFileInfo TargetFileInfo : Task.ImportInfo.TargetFiles)
  4301. {
  4302. const FString& Src = TargetFileInfo.SourceFile;
  4303. const FString& Dest = TargetFileInfo.TargetFile;
  4304. const FString SrcBaseFileName = FPaths::GetBaseFilename(Src);
  4305. // SlowTask
  4306. {
  4307. SlowTask.EnterProgressFrame(0.f, FText::FromString(FString::Printf(TEXT("%s %s")
  4308. , (ImportSetting.bExportMode ? TEXT("Exporting") : TEXT("Importing"))
  4309. , *SrcBaseFileName)));
  4310. if (SlowTask.ShouldCancel())
  4311. {
  4312. bSessionRequestToCancel = true;
  4313. bFailed = true;
  4314. ECB_LOG(Error, TEXT("[RunImportTasks] Canceling %s"), *SrcBaseFileName);
  4315. break;
  4316. }
  4317. }
  4318. MapRollbackSourceAndTarget(Task, *Src, *Dest);
  4319. if (bool* ImportReuslt = ImportedFilesResult.Find(FName(*Src)))
  4320. {
  4321. bool bSuccessImported = *ImportReuslt;
  4322. if (bSuccessImported)
  4323. {
  4324. ECB_LOG(Warning, TEXT(" => %s was already imported to %s, skip."), *Src, *Dest);
  4325. continue;
  4326. }
  4327. else
  4328. {
  4329. ECB_LOG(Error, TEXT(" => %s already failed to import"), *Src);
  4330. bFailed = true;
  4331. break;
  4332. }
  4333. }
  4334. ImportedFilesResult.FindOrAdd(FName(*Src)) = true;
  4335. #if ECB_FEA_IMPORT_ROLLBACK
  4336. const bool bNeedBackup = ImportSetting.bRollbackIfFailed && ImportSetting.bOverwriteExistingFiles && TargetFileInfo.bTargeFileWasExist && TargetFileInfo.bSourceTargetHasSameClass;
  4337. if (bNeedBackup)
  4338. {
  4339. const FString Backup = GenerateBackupFilePath(TargetFileInfo.TargetFile);
  4340. bool bBackuped = AddRollbackBackup(Task, *Dest, *Backup);
  4341. // SlowTask
  4342. {
  4343. SlowTask.EnterProgressFrame(1.f / 2, FText::FromString(FString::Printf(TEXT("Backing Up %s"), *SrcBaseFileName)));
  4344. if (SlowTask.ShouldCancel())
  4345. {
  4346. bSessionRequestToCancel = true;
  4347. bFailed = true;
  4348. ECB_LOG(Warning, TEXT("[RunImportTasks] Cancel backup %s"), *SrcBaseFileName);
  4349. break;
  4350. }
  4351. }
  4352. if (!bBackuped)
  4353. {
  4354. ECB_LOG(Error, TEXT(" => Failed to backup %s to %s"), *Dest, *Backup);
  4355. ImportedFilesResult.FindOrAdd(FName(*Src)) = false;
  4356. FailMessage = FString::Printf(TEXT("Failed to backup file: %s."), *FPaths::GetCleanFilename(TargetFileInfo.TargetFile));
  4357. bFailed = true;
  4358. break;
  4359. }
  4360. else
  4361. {
  4362. ECB_LOG(Display, TEXT(" => Success to backup %s to %s for rollback"), *Dest, *Backup);
  4363. }
  4364. }
  4365. #endif
  4366. const bool bCanSkip = !ImportSetting.bOverwriteExistingFiles && TargetFileInfo.bTargeFileWasExist;
  4367. if (bCanSkip)
  4368. {
  4369. ECB_LOG(Warning, TEXT(" => Skip to existing file: %s according to import setting"), *TargetFileInfo.TargetFile);
  4370. FString MainSourceFile = Task.GetMainSourceFilePath();
  4371. if (MainSourceFile.Equals(TargetFileInfo.SourceFile))
  4372. {
  4373. TargetFileInfo.bMainAsset = true;
  4374. }
  4375. TargetFileInfo.bSkipped = true;
  4376. ImportedFilesInSession.Add(TargetFileInfo);
  4377. continue;
  4378. }
  4379. if (ImportSetting.bOverwriteExistingFiles && TargetFileInfo.bTargeFileWasExist && !TargetFileInfo.bSourceTargetHasSameClass)
  4380. {
  4381. ECB_LOG(Error, TEXT(" => Not allow to overwrite %s with %s"), *Dest, *Src);
  4382. ImportedFilesResult.FindOrAdd(FName(*Src)) = false;
  4383. FailMessage = FString::Printf(TEXT("Not allow to overwrite %s."), *FPaths::GetCleanFilename(TargetFileInfo.TargetFile));
  4384. bFailed = true;
  4385. break;
  4386. }
  4387. const bool bOverwriteExisting = TargetFileInfo.bTargeFileWasExist;
  4388. FString* PluginRootForReloadPackage = nullptr;
  4389. {
  4390. if (TargetFileInfo.bPluginAsset)
  4391. {
  4392. PluginRootForReloadPackage = &TargetFileInfo.PluginRootWithSlash;
  4393. }
  4394. }
  4395. const bool bCopied = AddRollbackNewFile(Task, *Src, *Dest, bOverwriteExisting, PluginRootForReloadPackage);
  4396. // SlowTask
  4397. {
  4398. SlowTask.EnterProgressFrame(1.f / 2, FText::FromString(FString::Printf(TEXT("Adding %s"), *SrcBaseFileName)));
  4399. if (SlowTask.ShouldCancel())
  4400. {
  4401. bSessionRequestToCancel = true;
  4402. bFailed = true;
  4403. ECB_LOG(Warning, TEXT("[RunImportTasks] Cancel adding %s"), *SrcBaseFileName);
  4404. break;
  4405. }
  4406. }
  4407. if (!bCopied)
  4408. {
  4409. ECB_LOG(Error, TEXT(" => Failed to copy %s to %s"), *Src, *Dest);
  4410. ImportedFilesResult.FindOrAdd(FName(*Src)) = false;
  4411. FailMessage = FString::Printf(TEXT("Failed to copy file: %s."), *FPaths::GetCleanFilename(TargetFileInfo.SourceFile));
  4412. bFailed = true;
  4413. break;
  4414. }
  4415. else
  4416. {
  4417. ECB_LOG(Display, TEXT(" => Success to copy %s to %s"), *Src, *Dest);
  4418. // Is Main Asset
  4419. if (TargetFileInfo.SourceFile.Equals(Task.GetMainSourceFilePath()))
  4420. {
  4421. TargetFileInfo.bMainAsset = true;
  4422. }
  4423. // Or dependency
  4424. else
  4425. {
  4426. ++NumSuccessImportedDependencyAssets;
  4427. }
  4428. ImportedFilesInSession.Add(TargetFileInfo);
  4429. }
  4430. }
  4431. if (bFailed)
  4432. {
  4433. ECB_LOG(Error, TEXT("Failed to imported asset: %s"), *Task.GetMainSourceFilePath());
  4434. #if ECB_FEA_IMPORT_ROLLBACK
  4435. // Failed the task
  4436. MarkTaskFailed(Task, FailMessage);
  4437. #endif
  4438. }
  4439. else
  4440. {
  4441. SetTaskSuccessImported(Task);
  4442. ECB_LOG(Display, TEXT("Sucess to imported asset: %s"), *Task.GetMainSourceFilePath());
  4443. }
  4444. }
  4445. #if ECB_WIP_IMPORT_FOLDER_COLOR
  4446. // Import Folder Color
  4447. if (Task.IsSuccessImported() || Task.IsSkipped())
  4448. {
  4449. TMap<FString, FExtAssetImportTargetFolderInfo>& TargetFoldersMap = Task.ImportInfo.TargetColoredFolders;
  4450. ECB_LOG(Display, TEXT("Importing %d colored folders"), TargetFoldersMap.Num());
  4451. for (auto& Pair : TargetFoldersMap)
  4452. {
  4453. if (!ImportedColoredFolders.Contains(Pair.Key))
  4454. {
  4455. ECB_LOG(Display, TEXT(" Importing %s"), *Pair.Key);
  4456. auto& TargetFolderInfo = Pair.Value;
  4457. ECB_LOG(Display, TEXT(" %s, %s, %d, %s"), *TargetFolderInfo.TargetFolderPacakgeName, *TargetFolderInfo.TargetFolderColor.ToString(), TargetFolderInfo.bTargetFolderColorExist, *TargetFolderInfo.ExistingTargetFolderColor.ToString());
  4458. #if ECB_WIP_IMPORT_FOLDER_COLOR_OVERRIDE
  4459. AssetViewUtils::SetPathColor(TargetFolderInfo.TargetFolderPacakgeName, TOptional<FLinearColor>()); // clear PatchColors cache
  4460. AssetViewUtils::SetPathColor(TargetFolderInfo.TargetFolderPacakgeName, TOptional<FLinearColor>(FLinearColor(TargetFolderInfo.TargetFolderColor)));
  4461. #else
  4462. ExtContentBrowserUtils::SaveColor(TargetFolderInfo.TargetFolderPacakgeName, nullptr); // clear PatchColors cache
  4463. ExtContentBrowserUtils::SaveColor(TargetFolderInfo.TargetFolderPacakgeName, MakeShareable(new FLinearColor(TargetFolderInfo.TargetFolderColor)), true);
  4464. #endif
  4465. Task.RollbackInfo.Backup(TargetFolderInfo);
  4466. SessionRollbackInfo.Backup(TargetFolderInfo);
  4467. ImportedColoredFolders.Add(Pair.Key);
  4468. }
  4469. else
  4470. {
  4471. ECB_LOG(Display, TEXT(" Skipping %s"), *Pair.Key);
  4472. }
  4473. }
  4474. }
  4475. #endif
  4476. } // end of ImportTasks Loop
  4477. }
  4478. void ValidateSyncRedirectImportResult(FScopedSlowTask& SlowTask, TArray<FAssetData>& OutSuccessImportedAssets, TArray<FAssetData>& OutSuccessImportedMainAssets)
  4479. {
  4480. if (SlowTask.ShouldCancel())
  4481. {
  4482. ECB_LOG(Warning, TEXT("[ValidateAndSyncImportResult] Canceled"));
  4483. return;
  4484. }
  4485. // Check num of files to validate or sync
  4486. {
  4487. const bool bIncludeSkipeed = ImportSetting.bSyncExistingAssets;
  4488. int32 NumFilesToValidateOrSync = NumImportedFiles(bIncludeSkipeed);
  4489. ECB_LOG(Display, TEXT("[PostImportTasks] validate and sync %d imported files."), NumFilesToValidateOrSync);
  4490. if (NumFilesToValidateOrSync <= 0)
  4491. {
  4492. return;
  4493. }
  4494. }
  4495. // Register to asset registry, and redirect
  4496. {
  4497. SyncWithAssetRegistryAndRedirect(SlowTask);
  4498. }
  4499. // Validate by search with asset registry
  4500. TArray<FAssetData> SuccessImportedAssets;
  4501. TArray<FAssetData> SuccessImportedMainAssets;
  4502. {
  4503. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  4504. IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
  4505. const bool bIncludeSkipped = ImportSetting.bSyncExistingAssets;
  4506. TSet<FString> AllImportedFilePaths = GetImportedFilePaths(bIncludeSkipped);
  4507. TSet<FString> AllImportedMainAssetFilePaths = GetImportedMainAssetFilePaths(bIncludeSkipped);
  4508. for (const auto& ImportedFilePath : AllImportedFilePaths)
  4509. {
  4510. const bool bMainAsset = AllImportedMainAssetFilePaths.Contains(ImportedFilePath);
  4511. TArray<FAssetData> AssetDatas;
  4512. AssetRegistry.GetAssetsByPackageName(*FPackageName::FilenameToLongPackageName(ImportedFilePath), AssetDatas, /*bIncludeOnlyOnDiskAssets*/false);
  4513. if (AssetDatas.Num() > 0 && AssetDatas[0].IsValid())
  4514. {
  4515. SuccessImportedAssets.Emplace(AssetDatas[0]);
  4516. // Is Main Asset?
  4517. if (bMainAsset)
  4518. {
  4519. SuccessImportedMainAssets.Emplace(AssetDatas[0]);
  4520. // update task
  4521. SetTaskAssetDataByImportFilePath(ImportedFilePath, AssetDatas[0]);
  4522. }
  4523. }
  4524. else
  4525. {
  4526. #if ECB_FEA_IMPORT_ROLLBACK
  4527. // Make all tasks failed if has this file as dependency
  4528. ECB_LOG(Display, TEXT("Can't find asset data for imported:%s, mark task as failed."), *FPaths::GetCleanFilename(ImportedFilePath));
  4529. FString FailMessage = FString::Printf(TEXT("%s is not a valid asset."), *FPaths::GetCleanFilename(ImportedFilePath));
  4530. MarkTasksFailedByImportFilePath(ImportedFilePath, FailMessage);
  4531. #endif
  4532. }
  4533. }
  4534. }
  4535. // Sync Assets in Content Browser
  4536. if (ImportSetting.bSyncAssetsInContentBrowser)
  4537. {
  4538. ECB_LOG(Display, TEXT("Syncing %d assets in content browser."), SuccessImportedAssets.Num());
  4539. {
  4540. FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
  4541. const bool bSyncMainAssetsOnly = true; // todo: sync all imported assets (with dependencies) or main asset only?
  4542. if (bSyncMainAssetsOnly)
  4543. {
  4544. ContentBrowserModule.Get().SyncBrowserToAssets(SuccessImportedMainAssets);
  4545. }
  4546. else
  4547. {
  4548. ContentBrowserModule.Get().SyncBrowserToAssets(SuccessImportedAssets);
  4549. }
  4550. }
  4551. }
  4552. OutSuccessImportedAssets = SuccessImportedAssets;
  4553. OutSuccessImportedMainAssets = SuccessImportedMainAssets;
  4554. }
  4555. void PostImport(FScopedSlowTask& SlowTask)
  4556. {
  4557. // Rollback
  4558. if (bSessionRequestToCancel)
  4559. {
  4560. ECB_LOG(Warning, TEXT("[PostImport] SlowTask Cancelled, Rollback all taks!"));
  4561. MarkAllTaskFailed();
  4562. }
  4563. if (bSessionRequestToCancel || ImportSetting.bRollbackIfFailed)
  4564. {
  4565. bool bRollbackOnlyFailedTasks = false;/*true*/; // todo: make it an import option?
  4566. if (bRollbackOnlyFailedTasks)
  4567. {
  4568. RollbackFailedTasks(SlowTask);
  4569. }
  4570. else if (HasAnyTaskFailed())// rollback whole session if any task failed
  4571. {
  4572. RollbackSession(SlowTask);
  4573. }
  4574. }
  4575. // Cleanup: delete all backups files generated in this session
  4576. if (!ImportSetting.bDumpAsset)
  4577. {
  4578. Cleanup();
  4579. }
  4580. // Notify user
  4581. if (!ImportSetting.bSilentMode)
  4582. {
  4583. NotifyResult();
  4584. }
  4585. }
  4586. void LoadAssets(FScopedSlowTask& SlowTask, TArray<FAssetData>& InImportedAssets)
  4587. {
  4588. if (ImportSetting.bLoadAssetAfterImport && !bSessionRollbacked)
  4589. {
  4590. int32 NumAssets = InImportedAssets.Num();
  4591. if (NumAssets > 0)
  4592. {
  4593. float TaskStep = 1.0f / NumAssets;
  4594. for (auto& AssetData : InImportedAssets)
  4595. {
  4596. bool bSkipLoad = false;
  4597. if (AssetData.AssetClassPath.GetPackageName() == UWorld::StaticClass()->GetFName())
  4598. {
  4599. bSkipLoad = true;
  4600. continue;
  4601. }
  4602. FText TaskMessage = FText::FromString(FString::Printf(TEXT("%s %s..."), (bSkipLoad ? TEXT("Skip loading") : TEXT("Loading")), *AssetData.AssetName.ToString()));
  4603. SlowTask.EnterProgressFrame(TaskStep, TaskMessage);
  4604. if (!bSkipLoad)
  4605. {
  4606. AssetData.GetAsset();
  4607. }
  4608. }
  4609. }
  4610. #if 0
  4611. for (auto& Task : ImportTasks)
  4612. {
  4613. const FName AssetFilePath = *Task.GetMainSourceFilePath();
  4614. const bool bShouldLoadAsset = Task.IsSuccessImported() || (ImportSetting.bSyncExistingAssets && Task.IsSkipped());
  4615. if (bShouldLoadAsset && Task.ImportedMainAssetData.IsValid())
  4616. {
  4617. // don't load a level
  4618. if (FPaths::GetExtension(Task.GetMainSourceFilePath(), /*IncludeDot*/ true).Equals(FExtAssetSupport::MapPackageExtension, ESearchCase::IgnoreCase))
  4619. {
  4620. continue;
  4621. }
  4622. if (!Task.ImportedMainAssetData.IsAssetLoaded())
  4623. {
  4624. SlowTask.EnterProgressFrame(1.0f, FText::FromString(FString::Printf(TEXT("Loading %s..."), *FPaths::GetBaseFilename(AssetFilePath.ToString()))));
  4625. Task.ImportedMainAssetData.GetAsset();
  4626. }
  4627. }
  4628. }
  4629. #endif
  4630. }
  4631. }
  4632. void AddImportedAssetsToCollection(const TArray<FAssetData>& InAssets)
  4633. {
  4634. if (ImportSetting.bAddImportedAssetsToCollection)
  4635. {
  4636. FExtCollectionUtil::AddAssetsToCollection(InAssets
  4637. , ImportSetting.ImportedUAssetCollectionName
  4638. , ImportSetting.bUniqueCollectionNameForEachImportSession
  4639. );
  4640. }
  4641. }
  4642. void PlaceImportedMainAssetsToLevelEditor(const TArray<FAssetData>& InAssets)
  4643. {
  4644. if (ImportSetting.bPlaceImportedAssets)
  4645. {
  4646. FAssetDataUtil::PlaceToCurrentLevelViewport(InAssets);
  4647. }
  4648. }
  4649. void DumpImportedMainAssetsViaExportDialog(const TArray<FAssetData>& InAssets)
  4650. {
  4651. if (ImportSetting.bDumpAsset)
  4652. {
  4653. FAssetDataUtil::ExportAssetsWithDialog(InAssets);
  4654. }
  4655. }
  4656. void RunZipImportTasks()
  4657. {
  4658. int32 NumImportTasks = ImportTasks.Num();
  4659. ECB_LOG(Display, TEXT("[RunImportTasks] %d tasks."), NumImportTasks);
  4660. if (NumImportTasks <= 0)
  4661. {
  4662. return;
  4663. }
  4664. // All imported files including dependency file path to result map
  4665. TMap<FName, bool> ImportedFilesResult;
  4666. for (auto& Task : ImportTasks)
  4667. {
  4668. const FName AssetFilePath = *Task.GetMainSourceFilePath();
  4669. if (!Task.IsNotStarted())
  4670. {
  4671. continue;
  4672. }
  4673. ECB_LOG(Display, TEXT("Importing %s with %d dependencies will be imported)"), *AssetFilePath.ToString(), Task.ImportInfo.DependencyInfo.ValidDependentFiles.Num());
  4674. bool bFailed = false;
  4675. FString FailMessage;
  4676. for (FExtAssetImportTargetFileInfo TargetFileInfo : Task.ImportInfo.TargetFiles)
  4677. {
  4678. const FString& Src = TargetFileInfo.SourceFile;
  4679. const FString& Dest = TargetFileInfo.TargetFile;
  4680. if (bool* ImportReuslt = ImportedFilesResult.Find(FName(*Src)))
  4681. {
  4682. bool bSuccessImported = *ImportReuslt;
  4683. if (bSuccessImported)
  4684. {
  4685. ECB_LOG(Warning, TEXT(" => %s was already imported to %s, skip."), *Src, *Dest);
  4686. continue;
  4687. }
  4688. else
  4689. {
  4690. ECB_LOG(Error, TEXT(" => %s already failed to import"), *Src);
  4691. bFailed = true;
  4692. break;
  4693. }
  4694. }
  4695. ImportedFilesResult.FindOrAdd(FName(*Src)) = true;
  4696. {
  4697. ECB_LOG(Display, TEXT(" => Success to dry copy %s to %s"), *Src, *Dest);
  4698. // Is Main Asset
  4699. if (TargetFileInfo.SourceFile.Equals(Task.GetMainSourceFilePath()))
  4700. {
  4701. TargetFileInfo.bMainAsset = true;
  4702. }
  4703. // Or dependency
  4704. else
  4705. {
  4706. ++NumSuccessImportedDependencyAssets;
  4707. }
  4708. ImportedFilesInSession.Add(TargetFileInfo);
  4709. }
  4710. }
  4711. if (bFailed)
  4712. {
  4713. ECB_LOG(Error, TEXT("Failed to imported asset: %s"), *Task.GetMainSourceFilePath());
  4714. #if ECB_FEA_IMPORT_ROLLBACK
  4715. // Failed the task
  4716. MarkTaskFailed(Task, FailMessage);
  4717. #endif
  4718. }
  4719. else
  4720. {
  4721. SetTaskSuccessImported(Task);
  4722. ECB_LOG(Display, TEXT("Sucess to imported asset: %s"), *Task.GetMainSourceFilePath());
  4723. }
  4724. }
  4725. }
  4726. void ZipImportedFiles(const FString& SaveFileName)
  4727. {
  4728. TArray<FString> ImportedFilePaths = GetImportedFilePaths().Array();
  4729. if (ImportedFilePaths.Num() > 0)
  4730. {
  4731. IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
  4732. IFileHandle* ZipFile = PlatformFile.OpenWrite(*SaveFileName);
  4733. if (ZipFile)
  4734. {
  4735. FZipArchiveWriter* ZipWriter = new FZipArchiveWriter(ZipFile);
  4736. TArray<FString>& FilesToArchive = ImportedFilePaths;
  4737. const FString RootDir = ImportSetting.TargetContentDir;
  4738. const int32 SkipLen = RootDir.Len() + 1;
  4739. for (FString& FileName : FilesToArchive)
  4740. {
  4741. TArray<uint8> FileData;
  4742. FFileHelper::LoadFileToArray(FileData, *FileName);
  4743. FString RelativeFileName = FileName.Mid(SkipLen);
  4744. ZipWriter->AddFile(RelativeFileName, FileData, FDateTime::Now());
  4745. }
  4746. delete ZipWriter;
  4747. ZipWriter = nullptr;
  4748. }
  4749. }
  4750. }
  4751. void ZipSourceFiles(const FString& SaveFileName)
  4752. {
  4753. TArray<FExtAssetImportTargetFileInfo>& ImportedFileInfos = ImportedFilesInSession;
  4754. if (ImportedFileInfos.Num() > 0)
  4755. {
  4756. IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
  4757. IFileHandle* ZipFile = PlatformFile.OpenWrite(*SaveFileName);
  4758. if (ZipFile)
  4759. {
  4760. FZipArchiveWriter* ZipWriter = new FZipArchiveWriter(ZipFile);
  4761. for (const FExtAssetImportTargetFileInfo& FileInfo : ImportedFileInfos)
  4762. {
  4763. const FString& RootDir = FileInfo.AssetContentDir.ToString();
  4764. const int32 SkipLen = RootDir.Len() + 1;
  4765. FString FileName = FileInfo.SourceFile;
  4766. TArray<uint8> FileData;
  4767. FFileHelper::LoadFileToArray(FileData, *FileName);
  4768. FString RelativeFileName = FileName.Mid(SkipLen);
  4769. ZipWriter->AddFile(RelativeFileName, FileData, FDateTime::Now());
  4770. }
  4771. delete ZipWriter;
  4772. ZipWriter = nullptr;
  4773. }
  4774. }
  4775. }
  4776. bool HasAnyTaskFailed() const
  4777. {
  4778. for (const auto& Task : ImportTasks)
  4779. {
  4780. if (!Task.IsSuccessImported() && !Task.IsSkipped())
  4781. {
  4782. return true;
  4783. }
  4784. }
  4785. return false;
  4786. }
  4787. void RollbackFailedTasks(FScopedSlowTask& SlowTask)
  4788. {
  4789. bool bHasAnySuccessdTask = false;
  4790. for (auto& Task : ImportTasks)
  4791. {
  4792. if (!Task.IsSuccessImported() && !Task.IsSkipped())
  4793. {
  4794. SlowTask.EnterProgressFrame(0.0f, FText::FromString(FString::Printf(TEXT("Rollback %s"), *FPaths::GetBaseFilename(Task.GetMainSourceFilePath()))));
  4795. Task.Rollback();
  4796. }
  4797. else
  4798. {
  4799. bHasAnySuccessdTask = true;
  4800. }
  4801. }
  4802. // mark session rollbacked if all tasks failed or skipped
  4803. if (!bHasAnySuccessdTask)
  4804. {
  4805. bSessionRollbacked = true;
  4806. }
  4807. }
  4808. void RollbackSession(FScopedSlowTask& SlowTask)
  4809. {
  4810. SlowTask.EnterProgressFrame(0.0f, LOCTEXT("RollbackImportSession", "Rollback import session..."));
  4811. SessionRollbackInfo.RollbackAll();
  4812. bSessionRollbacked = true;
  4813. MarkAllTaskRollbacked();
  4814. }
  4815. void Cleanup()
  4816. {
  4817. SessionRollbackInfo.DeleteBackupFiles();
  4818. }
  4819. bool IsSessionRequestToCancel() const
  4820. {
  4821. return bSessionRequestToCancel;
  4822. }
  4823. bool IsSessionRollbacked() const
  4824. {
  4825. return bSessionRollbacked;
  4826. }
  4827. bool IsPackageRedirected(const FString& InAssetPackageName) const
  4828. {
  4829. return RedirectInfo.IsRedirected(InAssetPackageName);
  4830. }
  4831. int32 GetNumAllAssetsToImport() const
  4832. {
  4833. TSet<FString> AllSourceFiles;
  4834. for (const auto& Task : ImportTasks)
  4835. {
  4836. for (const FExtAssetImportTargetFileInfo& TargetFileInfo : Task.ImportInfo.TargetFiles)
  4837. {
  4838. AllSourceFiles.Emplace(TargetFileInfo.SourceFile);
  4839. }
  4840. }
  4841. return AllSourceFiles.Num();
  4842. }
  4843. int32 NumImportedFiles(bool bIncludeSkipped = false) const
  4844. {
  4845. int32 Num = 0;
  4846. for (const FExtAssetImportTargetFileInfo& TargetFileInfo : ImportedFilesInSession)
  4847. {
  4848. if (!TargetFileInfo.bSkipped || bIncludeSkipped)
  4849. {
  4850. ++Num;
  4851. }
  4852. }
  4853. return Num;
  4854. }
  4855. TSet<FString> GetImportedFilePaths(bool bIncludeSkipped = false) const
  4856. {
  4857. TSet<FString> ImportedFilePaths;
  4858. for (const FExtAssetImportTargetFileInfo& TargetFileInfo : ImportedFilesInSession)
  4859. {
  4860. if (!TargetFileInfo.bSkipped || bIncludeSkipped)
  4861. {
  4862. ImportedFilePaths.Add(TargetFileInfo.TargetFile);
  4863. }
  4864. }
  4865. return ImportedFilePaths;
  4866. }
  4867. TSet<FString> GetImportedMainAssetFilePaths(bool bIncludeSkipped = false) const
  4868. {
  4869. TSet<FString> ImportedFilePaths;
  4870. for (const FExtAssetImportTargetFileInfo& TargetFileInfo : ImportedFilesInSession)
  4871. {
  4872. if (TargetFileInfo.bMainAsset && (!TargetFileInfo.bSkipped || bIncludeSkipped))
  4873. {
  4874. ImportedFilePaths.Add(TargetFileInfo.TargetFile);
  4875. }
  4876. }
  4877. return ImportedFilePaths;
  4878. }
  4879. void NotifyResult()
  4880. {
  4881. struct Local
  4882. {
  4883. static FString GetMainAssetNameStrByTaskStatus(const TArray<FExtAssetImportTask>& InTasks, FExtAssetImportTask::EImportTaskStatus TaskStatus)
  4884. {
  4885. FString MainAssetNameStr;
  4886. int32 NumFound = 0;
  4887. for (auto& Task : InTasks)
  4888. {
  4889. if (Task.Status == TaskStatus)
  4890. {
  4891. if (NumFound == 0)
  4892. {
  4893. MainAssetNameStr = Task.SourceAssetData.AssetName.ToString();
  4894. }
  4895. ++NumFound;
  4896. }
  4897. }
  4898. if (NumFound > 0)
  4899. {
  4900. NumFound > 1 ? MainAssetNameStr += TEXT(", ...") : MainAssetNameStr += TEXT(",");
  4901. }
  4902. return MainAssetNameStr;
  4903. }
  4904. };
  4905. const int32 NumImportTaskSuccess = NumTasksByStatus(FExtAssetImportTask::EImportTaskStatus::Imported);
  4906. const int32 NumMainAssetsImportSuccess = NumImportTaskSuccess;
  4907. const int32 NumImportTaskSkipped = NumTasksByStatus(FExtAssetImportTask::EImportTaskStatus::Skipped);
  4908. const int32 NumImportTaskFailed = NumTasksByStatus(FExtAssetImportTask::EImportTaskStatus::Failed);
  4909. const int32 NumImportTaskRollbacked = NumTasksByStatus(FExtAssetImportTask::EImportTaskStatus::Rollbacked);
  4910. FString MainSuccessdAssetName = Local::GetMainAssetNameStrByTaskStatus(ImportTasks, FExtAssetImportTask::EImportTaskStatus::Imported);
  4911. FString MainSkippedAssetName = Local::GetMainAssetNameStrByTaskStatus(ImportTasks, FExtAssetImportTask::EImportTaskStatus::Skipped);
  4912. FString MainFailedAssetName = Local::GetMainAssetNameStrByTaskStatus(ImportTasks, FExtAssetImportTask::EImportTaskStatus::Failed);
  4913. FString MainRollabckedAssetName = Local::GetMainAssetNameStrByTaskStatus(ImportTasks, FExtAssetImportTask::EImportTaskStatus::Rollbacked);
  4914. FString MainAssetSuccessMessage = NumMainAssetsImportSuccess > 0 ? FString::Printf(TEXT("%s %d main asset(s) imported. "), *MainSuccessdAssetName, NumMainAssetsImportSuccess) : TEXT("");
  4915. FString DependencySuccessMessage = (!ImportSetting.bDirectCopyMode && NumSuccessImportedDependencyAssets > 0) ? FString::Printf(TEXT("%d dependencies imported. "), NumSuccessImportedDependencyAssets) : TEXT("");
  4916. FString FailedMessage = NumImportTaskFailed > 0 ? FString::Printf(TEXT("%s %d failed to import (%s). "), *MainFailedAssetName, NumImportTaskFailed, *LastFailedMessage) : TEXT("");
  4917. FString SkippedMessage = NumImportTaskSkipped > 0 ? FString::Printf(TEXT("%s %d skipped to import (%s). "), *MainSkippedAssetName, NumImportTaskSkipped, *LastSkipMessage) : TEXT("");
  4918. FString RollbackMessage = NumImportTaskRollbacked > 0 ? FString::Printf(TEXT("%s %d import task(s) rollbacked. "), *MainRollabckedAssetName, NumImportTaskRollbacked) : TEXT("");
  4919. FString ResultMessage = FString::Printf(TEXT("%s%s%s%s%s"), *MainAssetSuccessMessage, *DependencySuccessMessage, *FailedMessage, *SkippedMessage, *RollbackMessage);
  4920. ExtContentBrowserUtils::NotifyMessage(FText::FromString(ResultMessage), /*bPrintToConsole*/true);
  4921. }
  4922. public:
  4923. // Tasks
  4924. TArray<FExtAssetImportTask> ImportTasks;
  4925. // Rollback Info
  4926. FExtAssetRollbackInfo SessionRollbackInfo;
  4927. // Import Result
  4928. TArray<FExtAssetImportTargetFileInfo> ImportedFilesInSession; // All imported/skipped dependency files include main asset
  4929. private:
  4930. const FString GenerateBackupFilePath(const FString& InFilePath) const
  4931. {
  4932. FString SessionBackupFile = InFilePath + TEXT(".impbak"); // Fallback
  4933. //if (FPaths::MakePathRelativeTo(RelativePath, *ProjectContentDir)) // FPaths::MakePathRelativeTo assume InRelativeTo has end slash which not apply here
  4934. //if (InFilePath.StartsWith(ProjectContentDir))
  4935. if (FPathsUtil::IsFileInDir(InFilePath, ImportSetting.TargetContentDir))
  4936. {
  4937. FString RelativePath = InFilePath.Mid(ImportSetting.TargetContentDir.Len() + 1);
  4938. SessionBackupFile = FPaths::Combine(SesstionTempDir, RelativePath);
  4939. }
  4940. else // Cant make relative path, backup with unique file extension
  4941. {
  4942. FString UniqueId = FString::FromInt(FGuid::NewGuid().A);
  4943. FString UniqueFileExt = TEXT(".") + UniqueId;
  4944. SessionBackupFile = FPaths::Combine(SesstionTempDir, FPaths::GetBaseFilename(InFilePath), UniqueFileExt);
  4945. }
  4946. return SessionBackupFile;
  4947. }
  4948. void MapRollbackSourceAndTarget(FExtAssetImportTask& InTask, const FName& InSource, const FName& InTarget)
  4949. {
  4950. InTask.RollbackInfo.MapSourceToTarget(InSource, InTarget);
  4951. SessionRollbackInfo.MapSourceToTarget(InSource, InTarget);
  4952. }
  4953. bool AddRollbackBackup(FExtAssetImportTask& InTask, const FName& InTarget, const FName& InBackup)
  4954. {
  4955. // const bool bReplace = true;
  4956. // const bool bEvenIfReadOnly = false;
  4957. // const bool bAttributes = false;
  4958. // const bool bDoNotRetryOrError = false;
  4959. // bool bResult = IFileManager::Get().Move(/*Dest=*/*InBackup.ToString(), /*Src=*/ *InTarget.ToString(), bReplace, bEvenIfReadOnly, bAttributes, bDoNotRetryOrError);
  4960. const bool bReplace = true;
  4961. const bool bEvenIfReadOnly = true;
  4962. const bool bAttributes = true;
  4963. FCopyProgress* const CopyProgress = nullptr;
  4964. uint32 CopyResult = IFileManager::Get().Copy(/*Dest=*/ *InBackup.ToString(), /*Src=*/ *InTarget.ToString(), bReplace, bEvenIfReadOnly, bAttributes, CopyProgress, FILEREAD_AllowWrite, FILEWRITE_AllowRead);
  4965. bool bResult = CopyResult == COPY_OK;
  4966. if (bResult)
  4967. {
  4968. InTask.RollbackInfo.Backup(InTarget, InBackup);
  4969. SessionRollbackInfo.Backup(InTarget, InBackup);
  4970. }
  4971. return bResult;
  4972. }
  4973. bool AddRollbackNewFile(FExtAssetImportTask& InTask, const FName& InSource, const FName& InTarget, bool bReplaceExisting, FString* InPluginRootForReloadPackage /*todo: include all invalid roots? */)
  4974. {
  4975. TArray<UPackage*> ReplacedPackages;
  4976. if (bReplaceExisting)
  4977. {
  4978. FExtPackageUtils::UnloadPackage({ InTarget.ToString() }, &ReplacedPackages);
  4979. }
  4980. struct Local
  4981. {
  4982. static bool GetCreatedDir(const FName& InNewFile, FName& OutNewCreatedDir)
  4983. {
  4984. FString NewFile = InNewFile.ToString();
  4985. FString Dir = FPaths::GetPath(NewFile);
  4986. ECB_LOG(Display, TEXT("NewFile: %s, Dir: %s, Exist? %d"), *NewFile, *Dir, FPaths::DirectoryExists(Dir));
  4987. if (!FPaths::DirectoryExists(Dir))
  4988. {
  4989. FString CreatedDir = Dir;
  4990. int32 FindIndex = Dir.Find(TEXT("/"), ESearchCase::CaseSensitive, ESearchDir::FromEnd, Dir.Len());
  4991. for (; FindIndex != INDEX_NONE; FindIndex = Dir.Find(TEXT("/"), ESearchCase::CaseSensitive, ESearchDir::FromEnd, FindIndex))
  4992. {
  4993. FString SubDir = Dir.Mid(0, FindIndex);
  4994. const bool bSubDirExist = FPaths::DirectoryExists(SubDir);
  4995. if (bSubDirExist)
  4996. {
  4997. break;
  4998. }
  4999. CreatedDir = SubDir;
  5000. ECB_LOG(Display, TEXT(" => SubDir: %s, Exist? %d"), *SubDir, FPaths::DirectoryExists(SubDir));
  5001. }
  5002. OutNewCreatedDir = *CreatedDir;
  5003. return true;
  5004. }
  5005. return false;
  5006. }
  5007. };
  5008. FName NewCreatedDir;
  5009. const bool HasNewCreatedDir = Local::GetCreatedDir(InTarget, NewCreatedDir);
  5010. const bool bReplace = bReplaceExisting;
  5011. const bool bEvenIfReadOnly = false;
  5012. const bool bAttributes = false;
  5013. FCopyProgress* const CopyProgress = nullptr;
  5014. uint32 CopyResult = IFileManager::Get().Copy(/*Dest=*/ *InTarget.ToString(), /*Src=*/ *InSource.ToString(), bReplace, bEvenIfReadOnly, bAttributes, CopyProgress, FILEREAD_AllowWrite, FILEWRITE_AllowRead);
  5015. //uint32 CopyResult = IFileManager::Get().Copy(/*Dest=*/ *InTarget.ToString(), /*Src=*/ *InSource.ToString(), true, true);
  5016. bool bResult = CopyResult == COPY_OK;
  5017. if (bResult && !bReplaceExisting)
  5018. {
  5019. InTask.RollbackInfo.MapNewFile(InSource, InTarget, NewCreatedDir);
  5020. SessionRollbackInfo.MapNewFile(InSource, InTarget, NewCreatedDir);
  5021. }
  5022. if (bReplaceExisting && !bResult)
  5023. {
  5024. ECB_LOG(Error, TEXT("Failed to overwrite: %s"), *InTarget.ToString());
  5025. }
  5026. if (ImportSetting.bLoadAssetAfterImport && !ImportSetting.bSandboxMode)
  5027. {
  5028. struct Local
  5029. {
  5030. static void GetInvalidRoots(const TArray<UPackage*>& InReplacedPackages, TArray<FString>& OutInValidRootsWithSlash)
  5031. {
  5032. for (const UPackage* Package : InReplacedPackages)
  5033. {
  5034. if (!Package)
  5035. {
  5036. continue;
  5037. }
  5038. FString PackagePath = Package->GetPathName();
  5039. if (FPackageName::IsValidLongPackageName(PackagePath))
  5040. {
  5041. continue;
  5042. }
  5043. FString InvalidRoot = FExtAssetDataUtil::GetPackageRootFromFullPackagePath(PackagePath);
  5044. if (!InvalidRoot.IsEmpty())
  5045. {
  5046. OutInValidRootsWithSlash.AddUnique(InvalidRoot);
  5047. ECB_LOG(Display, TEXT("[GetInvalidRoots]: %s -> %s"), *PackagePath, *InvalidRoot);
  5048. }
  5049. }
  5050. }
  5051. static void MountInvalidRoots(const TArray<FString>& InValidRootsWithSlash)
  5052. {
  5053. for (const FString& MountPoint : InValidRootsWithSlash)
  5054. {
  5055. FPackageName::RegisterMountPoint(MountPoint /*TEXT("/SomeRoot/")*/, FExtAssetData::GetUAssetBrowserTempDir());
  5056. }
  5057. }
  5058. static void UnMountInvalidRoots(const TArray<FString>& InValidRootsWithSlash)
  5059. {
  5060. for (const FString& MountPoint : InValidRootsWithSlash)
  5061. {
  5062. FPackageName::UnRegisterMountPoint(MountPoint /*TEXT("/SomeRoot/")*/, FExtAssetData::GetUAssetBrowserTempDir());
  5063. }
  5064. }
  5065. static bool IsValidRoot(const FString& InRoot)
  5066. {
  5067. if (FPackageName::IsValidLongPackageName(InRoot))
  5068. {
  5069. return true;
  5070. }
  5071. return false;
  5072. }
  5073. };
  5074. TArray<FString> InValidRootsWithSlash;
  5075. //Local::GetInvalidRoots(ReplacedPackages, InValidRootsWithSlash);
  5076. if (InPluginRootForReloadPackage && !Local::IsValidRoot(*InPluginRootForReloadPackage))
  5077. {
  5078. InValidRootsWithSlash.AddUnique(*InPluginRootForReloadPackage);
  5079. }
  5080. Local::MountInvalidRoots(InValidRootsWithSlash);
  5081. {
  5082. // Make sure invalid root (e.g. not import from not installed plugin) is mount first before reload, get rid of warning messages
  5083. UPackageTools::ReloadPackages(ReplacedPackages);
  5084. }
  5085. Local::UnMountInvalidRoots(InValidRootsWithSlash);
  5086. }
  5087. return bResult;
  5088. }
  5089. void RollbackImportTask(FExtAssetImportTask& InTask)
  5090. {
  5091. ECB_LOG(Error, TEXT("[RollbackImportTask] %s, bAlreaydRoolbacked? %d"), *InTask.GetMainSourceFilePath(), InTask.IsRollbacked());
  5092. if (!InTask.IsRollbacked())
  5093. {
  5094. InTask.Rollback();
  5095. }
  5096. }
  5097. void MarkAllTaskFailed()
  5098. {
  5099. for (auto& Task : ImportTasks)
  5100. {
  5101. Task.Status = FExtAssetImportTask::EImportTaskStatus::Failed;
  5102. }
  5103. }
  5104. void MarkAllTaskRollbacked()
  5105. {
  5106. for (auto& Task : ImportTasks)
  5107. {
  5108. Task.Status = FExtAssetImportTask::EImportTaskStatus::Rollbacked;
  5109. }
  5110. }
  5111. void MarkTaskFailed(FExtAssetImportTask& InTask, const FString& InFailedMessage)
  5112. {
  5113. ECB_LOG(Error, TEXT("[MarkTaskFailed] %s"), *InTask.GetMainSourceFilePath());
  5114. LastFailedMessage = InFailedMessage;
  5115. InTask.Status = FExtAssetImportTask::EImportTaskStatus::Failed;
  5116. }
  5117. void MarkTasksFailedByImportFilePath(const FString& ImportedFilePath, const FString& InFailedMessage)
  5118. {
  5119. for (auto& Task : ImportTasks)
  5120. {
  5121. FExtAssetImportInfo& ImportInfo = Task.ImportInfo;
  5122. for (FExtAssetImportTargetFileInfo& TargetFileInfo : ImportInfo.TargetFiles)
  5123. {
  5124. if (TargetFileInfo.TargetFile.Equals(ImportedFilePath))
  5125. {
  5126. MarkTaskFailed(Task, InFailedMessage);
  5127. }
  5128. }
  5129. }
  5130. }
  5131. void SetTaskAssetDataByImportFilePath(const FString& ImportedFilePath, const FAssetData& InAssetData)
  5132. {
  5133. for (auto& Task : ImportTasks)
  5134. {
  5135. FExtAssetImportInfo& ImportInfo = Task.ImportInfo;
  5136. for (FExtAssetImportTargetFileInfo& TargetFileInfo : ImportInfo.TargetFiles)
  5137. {
  5138. if (TargetFileInfo.TargetFile.Equals(ImportedFilePath))
  5139. {
  5140. Task.ImportedMainAssetData = InAssetData;
  5141. }
  5142. }
  5143. }
  5144. }
  5145. void SetTaskSkipped(FExtAssetImportTask& InTask, const FString& InSkippedMessage)
  5146. {
  5147. InTask.Status = FExtAssetImportTask::EImportTaskStatus::Skipped;
  5148. LastSkipMessage = InSkippedMessage;
  5149. }
  5150. void SetTaskSuccessImported(FExtAssetImportTask& InTask)
  5151. {
  5152. InTask.Status = FExtAssetImportTask::EImportTaskStatus::Imported;
  5153. }
  5154. int32 NumTasksByStatus(FExtAssetImportTask::EImportTaskStatus InStatus) const
  5155. {
  5156. int32 NumTasks = 0;
  5157. for (const auto& Task : ImportTasks)
  5158. {
  5159. if (Task.Status == InStatus)
  5160. {
  5161. ++NumTasks;
  5162. }
  5163. }
  5164. return NumTasks;
  5165. }
  5166. void SyncWithAssetRegistryAndRedirect(FScopedSlowTask& SlowTask)
  5167. {
  5168. // Collect all imported package names and sandbox redirect map
  5169. RedirectInfo.CollectImportAndRedirectInfo(ImportedFilesInSession);
  5170. const bool bHasRediretRecord = RedirectInfo.HasRedirectRecord();
  5171. const bool bHasNoRediretPackages = RedirectInfo.HasNoRedirectImportPackages();
  5172. FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
  5173. IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
  5174. // Force rescan by file paths for fresh new imported assets
  5175. TArray<FString> ImportedFilePaths = GetImportedFilePaths().Array();
  5176. AssetRegistry.ScanFilesSynchronous(ImportedFilePaths, /*bForceRescan*/ true);
  5177. // Force rescan by package name in case package name still been cached
  5178. const TArray<FString>& AllImportedPackageNames = RedirectInfo.AllImportedPackageNames;
  5179. AssetRegistry.ScanFilesSynchronous(AllImportedPackageNames, /*bForceRescan*/true);
  5180. // Custom package name solver to redirecting game package to sandbox package for package linker
  5181. int32 PackageResolveDelegateIndex = INDEX_NONE;
  5182. if (bHasRediretRecord)
  5183. {
  5184. PackageResolveDelegateIndex = FCoreDelegates::PackageNameResolvers.Num();
  5185. TDelegate<bool(const FString&, FString&)> PackageResolveDelegate;
  5186. PackageResolveDelegate.BindLambda([this](const FString& InPackageName, FString& OutPackageName)
  5187. {
  5188. for (const auto& Pair : RedirectInfo.RedirectRecords)
  5189. {
  5190. const FString& OrigPackageName = Pair.Key;
  5191. const FString& RedirectToPackageName = Pair.Value.RedirectToPackageName;
  5192. if (InPackageName.Equals(OrigPackageName))
  5193. {
  5194. OutPackageName = RedirectToPackageName;
  5195. return true;
  5196. }
  5197. }
  5198. return false;
  5199. });
  5200. FCoreDelegates::PackageNameResolvers.Add(PackageResolveDelegate);
  5201. }
  5202. struct Local
  5203. {
  5204. static UPackage* FindLoadedPackage(const FString& PackageName)
  5205. {
  5206. UPackage* Package = FindPackage(NULL, *PackageName);
  5207. if (Package)
  5208. {
  5209. ECB_LOG(Display, TEXT("[FindPackage]%s Found, fullyload."), *PackageName);
  5210. Package->FullyLoad();
  5211. return Package;
  5212. }
  5213. return NULL;
  5214. }
  5215. static UPackage* FindOrLoadPackage(const FString& PackageName)
  5216. {
  5217. UPackage* Package = FindPackage(NULL, *PackageName);
  5218. if (Package)
  5219. {
  5220. ECB_LOG(Display, TEXT("[FindOrLoadPackge]%s Found, fullyload."), *PackageName);
  5221. Package->FullyLoad();
  5222. }
  5223. else
  5224. {
  5225. ECB_LOG(Display, TEXT("[FindOrLoadPackge]%s not found, LoadPackage."), *PackageName);
  5226. Package = LoadPackage(NULL, *PackageName, LOAD_None);
  5227. }
  5228. return Package;
  5229. }
  5230. };
  5231. if (bHasNoRediretPackages)
  5232. {
  5233. // Load package if not found in asset registry
  5234. const TArray<FString>& NoRedirectImportedPackageNames = RedirectInfo.NoRedirectImportedPackageNames;
  5235. for (const FString& ImportedPackageName : NoRedirectImportedPackageNames)
  5236. {
  5237. SlowTask.EnterProgressFrame(1.0f / NoRedirectImportedPackageNames.Num(), FText::FromString(FString::Printf(TEXT("Validating %s"), *FPaths::GetBaseFilename(ImportedPackageName))));
  5238. if (SlowTask.ShouldCancel())
  5239. {
  5240. bSessionRequestToCancel = true;
  5241. ECB_LOG(Warning, TEXT("[SyncWithAssetRegistry] Cancel validating %s"), *FPaths::GetBaseFilename(ImportedPackageName));
  5242. break;
  5243. }
  5244. TArray<FAssetData> AssetDatas;
  5245. const bool bFoundInAssetRegistry = AssetRegistry.GetAssetsByPackageName(*ImportedPackageName, AssetDatas, /*bIncludeOnlyOnDiskAssets*/false)
  5246. && AssetDatas.Num() > 0 && AssetDatas[0].IsValid();
  5247. if (!bFoundInAssetRegistry)
  5248. {
  5249. // Load in memory for sync
  5250. Local::FindOrLoadPackage(ImportedPackageName);
  5251. }
  5252. }
  5253. }
  5254. if (bHasRediretRecord)
  5255. {
  5256. TSet<UPackage*> OrigPackages;
  5257. TSet<UPackage*> RedirectToPackages;
  5258. TSet<UPackage*> NoSkippedRedirectPackagesForSave;
  5259. TArray<UObject*> NoSkippedRedirectObjects;
  5260. TMap<UObject*, UObject*> ObjectsReplaceMap;
  5261. const bool bUnloadRedirectToPackageForReload = true;
  5262. if (bUnloadRedirectToPackageForReload)
  5263. {
  5264. TArray<UPackage*> ReloadingPackages;
  5265. for (const auto& Pair : RedirectInfo.RedirectRecords)
  5266. {
  5267. const FRedirectRecord& RedirectRecord = Pair.Value;
  5268. const FString& RedirectToPackageName = RedirectRecord.RedirectToPackageName;
  5269. if (UPackage* RedirectToPackage = Local::FindLoadedPackage(RedirectToPackageName))
  5270. {
  5271. ReloadingPackages.Add(RedirectToPackage);
  5272. }
  5273. // const FString& OrigPackageName = Pair.Key;
  5274. // if (UPackage* OrigPackage = Local::FindLoadedPackage(OrigPackageName))
  5275. // {
  5276. // ReloadingPackages.Add(OrigPackage);
  5277. // }
  5278. }
  5279. UPackageTools::UnloadPackages(ReloadingPackages);
  5280. }
  5281. // Custom PackageResolve only works on non-editor
  5282. GIsEditor = false;
  5283. {
  5284. for (const auto& Pair : RedirectInfo.RedirectRecords)
  5285. {
  5286. const FString& OrigPackageName = Pair.Key;
  5287. const FRedirectRecord& RedirectRecord = Pair.Value;
  5288. const FString& RedirectToPackageName = RedirectRecord.RedirectToPackageName;
  5289. const bool bSkipped = RedirectInfo.SkippedRedirectToPackageNames.Contains(RedirectToPackageName);
  5290. UPackage* OrigPackage = Local::FindOrLoadPackage(OrigPackageName);
  5291. UPackage* RedirectToPackage = Local::FindOrLoadPackage(RedirectToPackageName);
  5292. if (RedirectToPackage)
  5293. {
  5294. const FString& AssetName = RedirectRecord.AssetName;
  5295. RedirectToPackages.Add(RedirectToPackage);
  5296. const bool bFindObjectByAssetName = true;
  5297. if (!bSkipped)
  5298. {
  5299. NoSkippedRedirectPackagesForSave.Add(RedirectToPackage);
  5300. if (bFindObjectByAssetName)
  5301. {
  5302. FString RedirectToObjectName = RedirectToPackageName + TEXT(".") + AssetName;
  5303. UObject* RedirectToObject = RedirectToPackage ? StaticFindObject(UObject::StaticClass(), nullptr, *RedirectToObjectName) : NULL;
  5304. if (RedirectToObject)
  5305. {
  5306. NoSkippedRedirectObjects.Add(RedirectToObject);
  5307. }
  5308. }
  5309. else
  5310. {
  5311. TArray<UPackage*> NewPackages = { RedirectToPackage };
  5312. TArray<UObject*> NewObjects;
  5313. UPackageTools::GetObjectsInPackages(&NewPackages, NewObjects);
  5314. if (NewObjects.Num() > 0)
  5315. {
  5316. NoSkippedRedirectObjects.Add(NewObjects[0]);
  5317. }
  5318. }
  5319. }
  5320. if (OrigPackage)
  5321. {
  5322. OrigPackages.Add(OrigPackage);
  5323. if (bFindObjectByAssetName)
  5324. {
  5325. FString OrigObjectName = OrigPackageName + TEXT(".") + AssetName;
  5326. UObject* OrigObject = OrigPackage ? StaticFindObject(UObject::StaticClass(), nullptr, *OrigObjectName) : NULL;
  5327. FString RedirectToObjectName = RedirectToPackageName + TEXT(".") + AssetName;
  5328. UObject* RedirectToObject = RedirectToPackage ? StaticFindObject(UObject::StaticClass(), nullptr, *RedirectToObjectName) : NULL;
  5329. if (OrigObject && RedirectToObject)
  5330. {
  5331. ObjectsReplaceMap.Add(OrigObject, RedirectToObject);
  5332. }
  5333. }
  5334. else
  5335. {
  5336. TArray<UPackage*> ExistingPackages = { OrigPackage };
  5337. TArray<UObject*> ExistingObjects;
  5338. UPackageTools::GetObjectsInPackages(&ExistingPackages, ExistingObjects);
  5339. TArray<UPackage*> NewPackages = { RedirectToPackage };
  5340. TArray<UObject*> NewObjects;
  5341. UPackageTools::GetObjectsInPackages(&NewPackages, NewObjects);
  5342. if (ExistingObjects.Num() > 0 && NewObjects.Num() > 0)
  5343. {
  5344. ObjectsReplaceMap.Add(ExistingObjects[0], NewObjects[0]);
  5345. }
  5346. }
  5347. }
  5348. }
  5349. }
  5350. struct FFixSubObjectReferencesHelper
  5351. {
  5352. static void FixSubObjectReferences(UObject* InObject, const TMap<UObject*, UObject*>& OldToNewInstanceMap)
  5353. {
  5354. // these may have the correct Outer but are not referenced by the CDO's UProperties
  5355. TArray<UObject*> SubObjects;
  5356. GetObjectsWithOuter(InObject, SubObjects, /*bIncludeNestedObjects*/true);
  5357. for (UObject* SubObject : SubObjects)
  5358. {
  5359. constexpr EArchiveReplaceObjectFlags ArFlags = (EArchiveReplaceObjectFlags::IgnoreOuterRef | EArchiveReplaceObjectFlags::TrackReplacedReferences);
  5360. FArchiveReplaceObjectRef<UObject> Replacer(SubObject, OldToNewInstanceMap, ArFlags);
  5361. }
  5362. // these may have the in-correct Outer but are incorrectly referenced by the CDO's UProperties
  5363. TSet<FInstancedSubObjRef> PropertySubObjectReferences;
  5364. UClass* ObjectClass = InObject->GetClass();
  5365. FFindInstancedReferenceSubobjectHelper::GetInstancedSubObjects(InObject, PropertySubObjectReferences);
  5366. for (UObject* PropertySubObject : PropertySubObjectReferences)
  5367. {
  5368. constexpr EArchiveReplaceObjectFlags ArFlags = (EArchiveReplaceObjectFlags::IgnoreOuterRef | EArchiveReplaceObjectFlags::TrackReplacedReferences);
  5369. FArchiveReplaceObjectRef<UObject> Replacer(PropertySubObject, OldToNewInstanceMap, ArFlags);
  5370. }
  5371. }
  5372. };
  5373. for (auto& RedirectToObject : NoSkippedRedirectObjects)
  5374. {
  5375. //UObject* RedirectToObject = Pair.Value;
  5376. {
  5377. constexpr EArchiveReplaceObjectFlags ArFlags = (EArchiveReplaceObjectFlags::IgnoreOuterRef | EArchiveReplaceObjectFlags::TrackReplacedReferences);
  5378. FArchiveReplaceObjectRef<UObject> ReplaceAr(RedirectToObject, ObjectsReplaceMap, ArFlags);
  5379. }
  5380. //FFixSubObjectReferencesHelper::FixSubObjectReferences(RedirectToObject, ObjectsReplaceMap);
  5381. // Fix Blueprint CDO
  5382. if (UBlueprint* BlueprintAsset = Cast<UBlueprint>(RedirectToObject))
  5383. {
  5384. UClass* BlueprintClass = BlueprintAsset->GeneratedClass;
  5385. if (BlueprintClass)
  5386. {
  5387. //FFixSubObjectReferencesHelper::FixSubObjectReferences(BlueprintClass, ObjectsReplaceMap);
  5388. ECB_LOG(Display, TEXT("[SyncWithAssetRegistryAndRedirect] Found Blueprint Class %s"), *BlueprintClass->GetName());
  5389. UObject* BlueprintCDO = BlueprintClass->ClassDefaultObject;
  5390. constexpr EArchiveReplaceObjectFlags ArFlags = (EArchiveReplaceObjectFlags::IgnoreOuterRef | EArchiveReplaceObjectFlags::TrackReplacedReferences);
  5391. FArchiveReplaceObjectRef<UObject> ReplaceAr(BlueprintCDO, ObjectsReplaceMap, ArFlags);
  5392. //FFixSubObjectReferencesHelper::FixSubObjectReferences(BlueprintCDO, ObjectsReplaceMap);
  5393. FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BlueprintAsset);
  5394. }
  5395. }
  5396. }
  5397. const bool bReplaceAllReferences = false;
  5398. if (bReplaceAllReferences)
  5399. {
  5400. // Main pass to go through and fix-up any references pointing to data from the old package to point to data from the new package
  5401. for (FThreadSafeObjectIterator ObjIter(UObject::StaticClass(), false, RF_NoFlags, EInternalObjectFlags::Garbage); ObjIter; ++ObjIter)
  5402. {
  5403. UObject* PotentialReferencer = *ObjIter;
  5404. // Mutating the old versions of classes can result in us replacing the SuperStruct pointer, which results
  5405. // in class layout change and subsequently crashes because instances will not match this new class layout:
  5406. if (UClass* AsClass = Cast<UClass>(PotentialReferencer))
  5407. {
  5408. if (AsClass->HasAnyClassFlags(CLASS_NewerVersionExists) || AsClass->HasAnyFlags(RF_NewerVersionExists))
  5409. {
  5410. continue;
  5411. }
  5412. }
  5413. constexpr EArchiveReplaceObjectFlags ArFlags = (EArchiveReplaceObjectFlags::IgnoreOuterRef | EArchiveReplaceObjectFlags::TrackReplacedReferences);
  5414. FArchiveReplaceObjectRef<UObject> ReplaceAr(PotentialReferencer, ObjectsReplaceMap, ArFlags);
  5415. }
  5416. }
  5417. const bool bResetOrigLoaders = true;
  5418. if (bResetOrigLoaders)
  5419. {
  5420. // Reset linkers of orig packages as they're no longer reliable
  5421. for (UPackage* OrigPackage : OrigPackages)
  5422. {
  5423. if (OrigPackage)
  5424. {
  5425. // Detach the linkers of any loaded packages so that SCC can overwrite the files...
  5426. ResetLoaders(OrigPackage);
  5427. OrigPackage->ClearFlags(RF_WasLoaded);
  5428. OrigPackage->bHasBeenFullyLoaded = false;
  5429. OrigPackage->GetMetaData()->RemoveMetaDataOutsidePackage();
  5430. }
  5431. }
  5432. }
  5433. }
  5434. GIsEditor = true;
  5435. if (RedirectToPackages.Num() > 0 && RedirectInfo.RedirectMap.Num() > 0)
  5436. {
  5437. // Remap soft references
  5438. IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
  5439. AssetTools.RenameReferencingSoftObjectPaths(NoSkippedRedirectPackagesForSave.Array(), RedirectInfo.RedirectMap);
  5440. // Save
  5441. TArray<UPackage*> PackagesToSave;
  5442. {
  5443. for (UPackage* Package : NoSkippedRedirectPackagesForSave)
  5444. {
  5445. if (IsValid(Package) && !UPackage::IsEmptyPackage(Package))
  5446. {
  5447. PackagesToSave.Add(Package);
  5448. }
  5449. }
  5450. }
  5451. TArray<UPackage*> FailedToSave;
  5452. {
  5453. const bool bCheckDirty = false;
  5454. const bool bPromptToSave = false;
  5455. FEditorFileUtils::PromptForCheckoutAndSave(/*RedirectToPackages*/PackagesToSave, bCheckDirty, bPromptToSave, &FailedToSave);
  5456. }
  5457. if (FailedToSave.Num() > 0)
  5458. {
  5459. ECB_LOG(Display, TEXT("%d packages failed to save."), FailedToSave.Num());
  5460. }
  5461. ECB_LOG(Display, TEXT("Saving %d redirected packages."), PackagesToSave.Num());
  5462. if (PackageResolveDelegateIndex != INDEX_NONE && PackageResolveDelegateIndex < FCoreDelegates::PackageNameResolvers.Num())
  5463. {
  5464. FCoreDelegates::PackageNameResolvers.RemoveAt(PackageResolveDelegateIndex);
  5465. }
  5466. }
  5467. }
  5468. }
  5469. private:
  5470. struct FRedirectRecord
  5471. {
  5472. FRedirectRecord()
  5473. : OrigPackageName(TEXT(""))
  5474. , RedirectToPackageName(TEXT(""))
  5475. , AssetName(TEXT(""))
  5476. , bMainAsset(false)
  5477. {
  5478. }
  5479. FRedirectRecord(const FString InOrigPackageName, const FString& InRedirectToPackageName, const FString& InAssetName, bool bInMainAsset)
  5480. : OrigPackageName(InOrigPackageName)
  5481. , RedirectToPackageName(InRedirectToPackageName)
  5482. , AssetName(InAssetName)
  5483. , bMainAsset(bInMainAsset)
  5484. {
  5485. }
  5486. FString OrigPackageName;
  5487. FString RedirectToPackageName;
  5488. FString AssetName;
  5489. bool bMainAsset = false;
  5490. };
  5491. struct FRedirectHelper
  5492. {
  5493. void CollectImportAndRedirectInfo(const TArray<FExtAssetImportTargetFileInfo>& InTargetFilesInSession)
  5494. {
  5495. static FString EmptyString(TEXT(""));
  5496. // Collect all imported package names and sandbox redirect map
  5497. for (const FExtAssetImportTargetFileInfo& TargetFileInfo : InTargetFilesInSession)
  5498. {
  5499. const bool bSkipped = TargetFileInfo.bSkipped;
  5500. const bool bMainAsset = TargetFileInfo.bMainAsset;
  5501. const FString& ImportedFilePath = TargetFileInfo.TargetFile;
  5502. const FName& ImportedAssetName = TargetFileInfo.SourceAssetName;
  5503. const FString ImportedPackageName = FPackageName::FilenameToLongPackageName(ImportedFilePath);
  5504. const bool bRedirect = TargetFileInfo.bRedirectMe;
  5505. const FString& RedirectFromPackageName = TargetFileInfo.RedirectFromPackageName;
  5506. const FString& RedirectToPackageName = TargetFileInfo.RedirectToPackageName;;
  5507. if (bRedirect && !RedirectFromPackageName.IsEmpty() && !RedirectToPackageName.IsEmpty())
  5508. {
  5509. //FString OrigPackageName = RedirectPackageRootFrom/*TEXT("/Game")*/ + ImportedPackageName.Mid(RedirectPackageRootTo.Len());
  5510. FString AssetName = ImportedAssetName != NAME_None ? ImportedAssetName.ToString() : FPaths::GetBaseFilename(ImportedPackageName);
  5511. // ? check(RedirectToPackageName equals ImportedPackageName)
  5512. AddRedirectRecord(RedirectFromPackageName, RedirectToPackageName, AssetName, bMainAsset);
  5513. }
  5514. else
  5515. {
  5516. NoRedirectImportedPackageNames.Add(ImportedPackageName);
  5517. }
  5518. AllImportedPackageNames.Add(ImportedPackageName);
  5519. if (bSkipped)
  5520. {
  5521. SkippedRedirectToPackageNames.Add(RedirectToPackageName);
  5522. }
  5523. }
  5524. }
  5525. void AddRedirectRecord(const FString& InOrigPackageName, const FString& InRedirectToPackageName, const FString& InAssetName, bool bInMainAsset)
  5526. {
  5527. if (!InOrigPackageName.Equals(InRedirectToPackageName))
  5528. {
  5529. FRedirectRecord Record(InOrigPackageName, InRedirectToPackageName, InAssetName, bInMainAsset);
  5530. RedirectRecords.FindOrAdd(InOrigPackageName) = Record;
  5531. RedirectMap.Add(FSoftObjectPath(InOrigPackageName + TEXT(".") + InAssetName), FSoftObjectPath(InRedirectToPackageName + TEXT(".") + InAssetName));
  5532. }
  5533. }
  5534. bool HasRedirectRecord() const
  5535. {
  5536. return RedirectRecords.Num() > 0;
  5537. }
  5538. bool HasNoRedirectImportPackages() const
  5539. {
  5540. return NoRedirectImportedPackageNames.Num() > 0;
  5541. }
  5542. bool IsRedirected(const FString& InPackageName) const
  5543. {
  5544. if (SkippedRedirectToPackageNames.Contains(InPackageName))
  5545. {
  5546. return false;
  5547. }
  5548. for (const auto& Pair : RedirectRecords)
  5549. {
  5550. const auto& Record = Pair.Value;
  5551. if (Record.RedirectToPackageName.Equals(InPackageName, ESearchCase::CaseSensitive))
  5552. {
  5553. return true;
  5554. }
  5555. }
  5556. return false;
  5557. }
  5558. TMap<FString, FRedirectRecord> RedirectRecords; // OrigPackageName => Record
  5559. TMap<FSoftObjectPath, FSoftObjectPath> RedirectMap;// /Game/SomePackage.SomeAssetName => /UAssetBrowser/Sandbox/SomePackage.SomeAssetName
  5560. TArray<FString> AllImportedPackageNames;
  5561. TArray<FString> NoRedirectImportedPackageNames;
  5562. TSet<FString> SkippedRedirectToPackageNames;
  5563. };
  5564. FRedirectHelper RedirectInfo;
  5565. FUAssetImportSetting ImportSetting;
  5566. FString SesstionTempDir;
  5567. bool bSessionRollbacked = false;
  5568. bool bSessionRequestToCancel = false;
  5569. // Import Result
  5570. int32 NumSuccessImportedDependencyAssets = 0;
  5571. FString LastSkipMessage;
  5572. FString LastFailedMessage;
  5573. };
  5574. ////////////////////////////////////////////////////
  5575. void FExtAssetImporter::ImportAssets(const TArray<FExtAssetData>& InAssetDatas, const FUAssetImportSetting& ImportSetting)
  5576. {
  5577. if (!ValidateImportSetting(ImportSetting))
  5578. {
  5579. return;
  5580. }
  5581. #if ECB_WIP_IMPORT_TO_PLUGIN_FOLDER
  5582. if (ImportSetting.bImportToPluginFolder && ImportSetting.bWarnBeforeImportToPluginFolder && !ImportSetting.bSandboxMode && !ImportSetting.bFlatMode)
  5583. {
  5584. FUAssetImportSetting ImportToProjectSetting = ImportSetting;
  5585. ImportToProjectSetting.TargetContentDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
  5586. if (IsValidImportToPluginName(ImportSetting.ImportToPluginName))
  5587. {
  5588. EAppReturnType::Type YesNoCancelReply = FMessageDialog::Open(EAppMsgType::YesNoCancel,
  5589. FText::Format(
  5590. LOCTEXT("Prompt_ImportToPluginOrProjectContentFolder", "Would you like to import assets into plugin: {0} ?\n\n Yes: Import to plugin's content folder\n No: Import to project's content folder\n Cancel: Abort import"),
  5591. FText::FromName(ImportSetting.ImportToPluginName)));
  5592. switch (YesNoCancelReply)
  5593. {
  5594. case EAppReturnType::Yes:
  5595. DoImportAssets(InAssetDatas, ImportSetting);
  5596. break;
  5597. case EAppReturnType::No:
  5598. DoImportAssets(InAssetDatas, ImportToProjectSetting);
  5599. break;
  5600. case EAppReturnType::Cancel:
  5601. return;
  5602. }
  5603. }
  5604. #if ECB_TODO // todo: option to import to project when plugin is invalid
  5605. else
  5606. {
  5607. EAppReturnType::Type YesNoReply = FMessageDialog::Open(EAppMsgType::YesNo,
  5608. FText::Format(
  5609. LOCTEXT("Prompt_ImportToPluginOrProjectContentFolder", "Plugin {0} is not a valid target, would you like to import to project's content folder instead?\n\nYes: Import to project's content folder\nNo: Abort import"),
  5610. FText::FromName(ImportSetting.ImportToPluginName)));
  5611. switch (YesNoReply)
  5612. {
  5613. case EAppReturnType::Yes:
  5614. DoImportAssets(InAssetDatas, ImportToProjectSetting);
  5615. break;
  5616. case EAppReturnType::No:
  5617. return;
  5618. }
  5619. }
  5620. #endif
  5621. return;
  5622. }
  5623. #endif
  5624. DoImportAssets(InAssetDatas, ImportSetting);
  5625. }
  5626. void FExtAssetImporter::DoImportAssets(const TArray<FExtAssetData>& InAssetDatas, const FUAssetImportSetting& ImportSetting)
  5627. {
  5628. int32 NumMainAssetsToImport = InAssetDatas.Num();
  5629. ECB_LOG(Display, TEXT("========== Importing %d Assets ============"), NumMainAssetsToImport);
  5630. if (NumMainAssetsToImport <= 0)
  5631. {
  5632. return;
  5633. }
  5634. FExtAssetImportSession Session(ImportSetting);
  5635. Session.ImportAssets(InAssetDatas);
  5636. }
  5637. void FExtAssetImporter::ImportAssetsByFilePaths(const TArray<FString>& InAssetFilePaths, const FUAssetImportSetting& ImportSetting)
  5638. {
  5639. if (!ValidateImportSetting(ImportSetting))
  5640. {
  5641. return;
  5642. }
  5643. #if ECB_WIP_IMPORT_TO_PLUGIN_FOLDER
  5644. if (ImportSetting.bImportToPluginFolder && !IsValidImportToPluginName(ImportSetting.ImportToPluginName))
  5645. {
  5646. return;
  5647. }
  5648. #endif
  5649. TArray<FExtAssetData> AssetDatas;
  5650. for (const FString& AssetFilePath : InAssetFilePaths)
  5651. {
  5652. FExtAssetData* AssetData = FExtContentBrowserSingleton::GetAssetRegistry().GetOrCacheAssetByFilePath(*AssetFilePath, /*bDelayParse */false);
  5653. if (AssetData && AssetData->IsValid())
  5654. {
  5655. AssetDatas.Add(*AssetData);
  5656. }
  5657. }
  5658. DoImportAssets(AssetDatas, ImportSetting);
  5659. }
  5660. void FExtAssetImporter::ImportAssetsToFolderPackagePath(const TArray<FExtAssetData>& InAssetDatas, const FUAssetImportSetting& InImportSetting, const FString& InDestPackagePath)
  5661. {
  5662. UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
  5663. FContentBrowserItemPath DropFolderItemPath(*InDestPackagePath, EContentBrowserPathType::Virtual); //Assume Virtual Path
  5664. FString DestPackagePath = DropFolderItemPath.HasInternalPath()? *DropFolderItemPath.GetInternalPathString() : *DropFolderItemPath.GetVirtualPathString();
  5665. ECB_LOG(Display, TEXT("[ImportAssetsToFolderPackagePath] DropFolderItem: DestPackagePath %s"), *DestPackagePath);
  5666. FString FolderPath = FPaths::ConvertRelativePathToFull(FPackageName::LongPackageNameToFilename(DestPackagePath));
  5667. FPaths::NormalizeDirectoryName(FolderPath); // no ending slash
  5668. FUAssetImportSetting ImportSetting = InImportSetting;
  5669. ImportSetting.TargetContentDir = FolderPath;
  5670. ECB_LOG(Display, TEXT("[ImportAssetsWithPathPicker] %s =>> %s"), *InDestPackagePath, *FolderPath);
  5671. FExtAssetImporter::DoImportAssets(InAssetDatas, ImportSetting);
  5672. }
  5673. void FExtAssetImporter::ImportAssetsWithPathPicker(const TArray<FExtAssetData>& InAssetDatas, const FUAssetImportSetting& InImportSetting)
  5674. {
  5675. // Prepare Content Root Dir with Dialog
  5676. {
  5677. TSharedPtr<SWindow> PickContentPathWidget;
  5678. SAssignNew(PickContentPathWidget, SWindow)
  5679. .Title(LOCTEXT("SelectPath", "Select Path"))
  5680. .ToolTipText(LOCTEXT("SelectPathTooltip", "Select the path where the uasset files will be imported to"))
  5681. .ClientSize(FVector2D(400, 400));
  5682. TSharedPtr<SContentBrowserPathPicker> ContentBrowserPathPicker;
  5683. PickContentPathWidget->SetContent
  5684. (
  5685. SAssignNew(ContentBrowserPathPicker, SContentBrowserPathPicker, PickContentPathWidget)
  5686. .HeadingText(LOCTEXT("ContentBrowserPathPicker_Heading", "Folder Name"))
  5687. .CreateButtonText(LOCTEXT("ContentBrowserPathPicker_ButtonLabel", "Import"))
  5688. .OnCreateAssetAction
  5689. (
  5690. FOnPathChosen::CreateLambda([=](const FString& InDestPackagePath) {
  5691. FExtAssetImporter::ImportAssetsToFolderPackagePath(InAssetDatas, InImportSetting, InDestPackagePath);
  5692. })
  5693. )
  5694. );
  5695. TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow();
  5696. if (RootWindow.IsValid())
  5697. {
  5698. FSlateApplication::Get().AddWindowAsNativeChild(PickContentPathWidget.ToSharedRef(), RootWindow.ToSharedRef());
  5699. }
  5700. else
  5701. {
  5702. FSlateApplication::Get().AddWindow(PickContentPathWidget.ToSharedRef());
  5703. }
  5704. }
  5705. }
  5706. void FExtAssetImporter::ImportProjectFolderColors(const FString& InRootOrHost)
  5707. {
  5708. FString AssetContentDir;
  5709. if (FExtContentBrowserSingleton::GetAssetRegistry().IsRootFolder(InRootOrHost))
  5710. {
  5711. FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPathInfo(InRootOrHost, nullptr, nullptr, &AssetContentDir);
  5712. }
  5713. else if (const FName* AssetContentDirPtr = FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetContentRootHostDirs().Find(*InRootOrHost))
  5714. {
  5715. AssetContentDir = AssetContentDirPtr->ToString();
  5716. }
  5717. if (AssetContentDir.Len() > 0)
  5718. {
  5719. if (const FName* ConfigDirName = FExtContentBrowserSingleton::GetAssetRegistry().GetCachedAssetContentRootConfigDirs().Find(*AssetContentDir))
  5720. {
  5721. TMap<FName, FLinearColor> PackagePathColors;
  5722. if (FExtConfigUtil::LoadPathColors(AssetContentDir, ConfigDirName->ToString(), PackagePathColors, false))
  5723. {
  5724. ECB_LOG(Display, TEXT("Applying %d colored folders"), PackagePathColors.Num());
  5725. for (auto& Pair : PackagePathColors)
  5726. {
  5727. const FString PackagePath = Pair.Key.ToString();
  5728. const FLinearColor& FolderColor = Pair.Value;
  5729. //if (!ImportedColoredFolders.Contains(Pair.Key)) Is Valid Package Path
  5730. FString FolderPath;
  5731. if (FPackageName::TryConvertLongPackageNameToFilename(PackagePath, FolderPath) && FPaths::DirectoryExists(FolderPath))
  5732. {
  5733. ECB_LOG(Display, TEXT(" Applying %s - %s"), *PackagePath, *FolderColor.ToString());
  5734. #if ECB_WIP_IMPORT_FOLDER_COLOR_OVERRIDE
  5735. AssetViewUtils::SetPathColor(PackagePath, TOptional<FLinearColor>()); // clear PatchColors cache
  5736. AssetViewUtils::SetPathColor(PackagePath, TOptional<FLinearColor>(FLinearColor(FolderColor)));
  5737. #else
  5738. ExtContentBrowserUtils::SetPathColor(PackagePath, nullptr); // clear PatchColors cache
  5739. ExtContentBrowserUtils::SetPathColor(PackagePath, MakeShareable(new FLinearColor(FolderColor)), true);
  5740. #endif
  5741. }
  5742. else
  5743. {
  5744. ECB_LOG(Display, TEXT(" Skipping %s"), *PackagePath);
  5745. }
  5746. }
  5747. }
  5748. }
  5749. }
  5750. }
  5751. void FExtAssetImporter::ExportAssets(const TArray<FExtAssetData>& InAssetDatas)
  5752. {
  5753. bool bFolderSelected = false;
  5754. IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
  5755. if (DesktopPlatform)
  5756. {
  5757. void* TopWindowHandle = FSlateApplication::Get().GetActiveTopLevelWindow().IsValid() ? FSlateApplication::Get().GetActiveTopLevelWindow()->GetNativeWindow()->GetOSWindowHandle() : nullptr;
  5758. FString DefaultPath;
  5759. FString FolderName;
  5760. bFolderSelected = DesktopPlatform->OpenDirectoryDialog(
  5761. TopWindowHandle,
  5762. /*Title=*/TEXT("Select a folder to export uassets"),
  5763. DefaultPath,
  5764. FolderName
  5765. );
  5766. if (bFolderSelected)
  5767. {
  5768. ECB_LOG(Display, TEXT("Select a folder to export uassets: %s"), *FolderName);
  5769. const FUAssetImportSetting ExportSetting = FUAssetImportSetting::GetDefaultExportSetting(FolderName);
  5770. int32 NumAssetsToExport = InAssetDatas.Num();
  5771. ECB_LOG(Display, TEXT("========== Exporting %d Assets ============"), NumAssetsToExport);
  5772. if (NumAssetsToExport <= 0)
  5773. {
  5774. return;
  5775. }
  5776. FExtAssetImportSession Session(ExportSetting);
  5777. // const int32 TotalSteps = 4;
  5778. // FScopedSlowTask SlowTask(TotalSteps, FText::Format(LOCTEXT("GatheringDependencies", "Gathering dependencies for {0} Assets..."), FText::AsNumber(NumAssetsToExport)));
  5779. // SlowTask.MakeDialog();
  5780. ECB_LOG(Display, TEXT("------------ Phase 1: Gathering dependencies -----------------"));
  5781. {
  5782. // FString FirstAssetFilePath = InAssetDatas[0].PackageFilePath.ToString();
  5783. // FString Progress = FString::Printf(TEXT("Gathering dependencies for %s%s...")
  5784. // , *FPaths::GetBaseFilename(FirstAssetFilePath)
  5785. // , (NumAssetsToExport ? TEXT("") : *FString::Printf(TEXT(" and other %d assets"), NumAssetsToExport - 1)));
  5786. // SlowTask.EnterProgressFrame(1, FText::FromString(Progress));
  5787. Session.PrepareImportTasksWithDependencies(InAssetDatas);
  5788. if (Session.IsSessionRequestToCancel())
  5789. {
  5790. FString FirstAssetFilePath = InAssetDatas[0].PackageFilePath.ToString();
  5791. FString AbortMessage = FString::Printf(TEXT("Export %s%s aborted!")
  5792. , *FPaths::GetBaseFilename(FirstAssetFilePath)
  5793. , (NumAssetsToExport ? TEXT("") : *FString::Printf(TEXT(" and other %d assets"), NumAssetsToExport - 1)));
  5794. ExtContentBrowserUtils::NotifyMessage(FText::FromString(AbortMessage));
  5795. return;
  5796. }
  5797. }
  5798. ECB_LOG(Display, TEXT("------------ Phase 2: Exporting asset files -----------------"));
  5799. {
  5800. // SlowTask.EnterProgressFrame(1, LOCTEXT("ExportingAssets_ImportingFile", "Exporting files..."));
  5801. // Session.RunImportTasks();
  5802. const int32 NumAllAsssetsToExport = Session.GetNumAllAssetsToImport();
  5803. const float TotalSteps = NumAllAsssetsToExport + 1;
  5804. TSharedPtr<FExtAssetCoreUtil::FExtAssetFeedbackContext> FeedbackContext(new FExtAssetCoreUtil::FExtAssetFeedbackContext);
  5805. FScopedSlowTask SlowTask(TotalSteps, FText::Format(LOCTEXT("ExportingAssets_ImportingFile", "Exporting {0} Assets..."), FText::AsNumber(NumAllAsssetsToExport))
  5806. , /*bEnabled*/ true,
  5807. *FeedbackContext.Get()
  5808. );
  5809. SlowTask.MakeDialog(/*bShowCancelDialog =*/ true);
  5810. Session.RunImportTasks(SlowTask);
  5811. }
  5812. ECB_LOG(Display, TEXT("------------ Phase 3: Rollback and cleanup -----------------"));
  5813. {
  5814. // SlowTask.EnterProgressFrame(1, LOCTEXT("ExportingAssets_Finishing", "Finishing export..."));
  5815. // Session.PostImport();
  5816. FScopedSlowTask SlowTask(1, LOCTEXT("ExportingAssets_Finishing", "Finish Exporting..."));
  5817. SlowTask.MakeDialog(/*bShowCancelDialog =*/ false);
  5818. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  5819. if (/*!Session.IfAllTasksFailed() && */ ExtContentBrowserSetting->bOpenFolderAfterExport)
  5820. {
  5821. FPlatformProcess::ExploreFolder(*FolderName);
  5822. }
  5823. }
  5824. }
  5825. }
  5826. }
  5827. void FExtAssetImporter::ZipUpSourceAssets(const TArray<FExtAssetData>& InAssetDatas)
  5828. {
  5829. int32 NumAssetsToExport = InAssetDatas.Num();
  5830. ECB_LOG(Display, TEXT("========== Exporting %d Assets ============"), NumAssetsToExport);
  5831. if (NumAssetsToExport <= 0)
  5832. {
  5833. return;
  5834. }
  5835. bool bOpened = false;
  5836. TArray<FString> SaveFilenames;
  5837. IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
  5838. if (DesktopPlatform != NULL)
  5839. {
  5840. FString DefaultFileName = FPaths::GetBaseFilename(InAssetDatas[0].PackageFilePath.ToString()) + TEXT(".zip");
  5841. bOpened = DesktopPlatform->SaveFileDialog(
  5842. NULL,
  5843. LOCTEXT("ZipUpAssets", "Zip file location").ToString(),
  5844. TEXT(""),
  5845. DefaultFileName,
  5846. TEXT("Zip file|*.zip"),
  5847. EFileDialogFlags::None,
  5848. SaveFilenames);
  5849. }
  5850. if (bOpened && SaveFilenames.Num() > 0)
  5851. {
  5852. FString SaveFileName = SaveFilenames[0];
  5853. FString SaveFolder = FPaths::GetPath(SaveFileName);
  5854. {
  5855. ECB_LOG(Display, TEXT("Select a zip file to export uassets: %s"), *SaveFileName);
  5856. FUAssetImportSetting ExportSetting = FUAssetImportSetting::GetZipExportSetting();
  5857. ExportSetting.TargetContentDir = FExtAssetData::GetZipExportSessionTempDir();
  5858. FExtAssetImportSession Session(ExportSetting);
  5859. const int32 TotalSteps = 3;
  5860. FScopedSlowTask SlowTask(TotalSteps, FText::Format(LOCTEXT("GatheringDependencies", "Gathering dependencies for {0} Assets..."), FText::AsNumber(NumAssetsToExport)));
  5861. SlowTask.MakeDialog();
  5862. ECB_LOG(Display, TEXT("------------ Phase 1: Gathering dependencies -----------------"));
  5863. {
  5864. FString FirstAssetFilePath = InAssetDatas[0].PackageFilePath.ToString();
  5865. FString Progress = FString::Printf(TEXT("Gathering dependencies for %s%s...")
  5866. , *FPaths::GetBaseFilename(FirstAssetFilePath)
  5867. , (NumAssetsToExport ? TEXT("") : *FString::Printf(TEXT(" and other %d assets"), NumAssetsToExport - 1)));
  5868. SlowTask.EnterProgressFrame(1, FText::FromString(Progress));
  5869. Session.PrepareImportTasksWithDependencies(InAssetDatas);
  5870. }
  5871. ECB_LOG(Display, TEXT("------------ Phase 2: Exporting asset files -----------------"));
  5872. {
  5873. SlowTask.EnterProgressFrame(1, LOCTEXT("ExportingAssets_ImportingFile", "Exporting files..."));
  5874. Session.RunZipImportTasks();
  5875. }
  5876. ECB_LOG(Display, TEXT("------------ Phase 3: Ziping -----------------"));
  5877. {
  5878. SlowTask.EnterProgressFrame(1, LOCTEXT("ExportingAssets_ZipFile", "Archiving files..."));
  5879. Session.ZipSourceFiles(SaveFileName);
  5880. }
  5881. ECB_LOG(Display, TEXT("------------ Phase 4: cleanup -----------------"));
  5882. {
  5883. SlowTask.EnterProgressFrame(1, LOCTEXT("ExportingAssets_Finishing", "Finishing export..."));
  5884. Session.Cleanup();
  5885. Session.NotifyResult();
  5886. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  5887. if (/*!Session.IfAllTasksFailed() && */ ExtContentBrowserSetting->bOpenFolderAfterExport)
  5888. {
  5889. FPlatformProcess::ExploreFolder(*SaveFolder);
  5890. }
  5891. }
  5892. }
  5893. }
  5894. }
  5895. bool FExtAssetImporter::IsValidImportToPluginName(const FName& InPluginName, FString* OutInValidReason, FString *OutValidPluginContentDir)
  5896. {
  5897. if (InPluginName == NAME_None)
  5898. {
  5899. if (OutInValidReason)
  5900. {
  5901. *OutInValidReason = FString::Printf(TEXT("No plugin been specified for import!"), *InPluginName.ToString());
  5902. }
  5903. return false;
  5904. }
  5905. TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(InPluginName.ToString());
  5906. if (!Plugin.IsValid())
  5907. {
  5908. if (OutInValidReason)
  5909. {
  5910. *OutInValidReason = FString::Printf(TEXT("Import to Plugin: %s not found!"), *InPluginName.ToString());
  5911. }
  5912. return false;
  5913. }
  5914. FString PluginContentDir = FPaths::ConvertRelativePathToFull(Plugin->GetContentDir());
  5915. FString ProjectDIr = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir());
  5916. ECB_LOG(Display, TEXT("[FExtAssetImporter::IsValidImportToPluginName] PluginContentDir: %s, PrjectDir: %s"), *PluginContentDir, *ProjectDIr);
  5917. if (PluginContentDir.StartsWith(ProjectDIr))
  5918. {
  5919. if (OutValidPluginContentDir)
  5920. {
  5921. *OutValidPluginContentDir = PluginContentDir;
  5922. }
  5923. return true;
  5924. }
  5925. else
  5926. {
  5927. if (OutInValidReason)
  5928. {
  5929. *OutInValidReason = FString::Printf(TEXT("Import to Plugin: %s should be inside project folder!"), *InPluginName.ToString());
  5930. }
  5931. return false;
  5932. }
  5933. return false;
  5934. }
  5935. bool FExtAssetImporter::ValidateImportSetting(const FUAssetImportSetting& ImportSetting)
  5936. {
  5937. if (!ImportSetting.IsValid())
  5938. {
  5939. ExtContentBrowserUtils::NotifyMessage(ImportSetting.GetInvalidReason());
  5940. return false;
  5941. }
  5942. if (ImportSetting.bImportToPluginFolder && ImportSetting.bFlatMode)
  5943. {
  5944. ExtContentBrowserUtils::NotifyMessage(LOCTEXT("", "Flat Import to Plugin is not supported!"));
  5945. return false;
  5946. }
  5947. return true;
  5948. }
  5949. //--------------------------------------------------
  5950. // FExtAssetImportSetting Impl
  5951. //
  5952. FUAssetImportSetting::FUAssetImportSetting()
  5953. {
  5954. TargetContentDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
  5955. FPaths::NormalizeDirectoryName(TargetContentDir);
  5956. }
  5957. FUAssetImportSetting::FUAssetImportSetting(const FString& InTargetContentDir)
  5958. : TargetContentDir(InTargetContentDir)
  5959. {
  5960. }
  5961. FUAssetImportSetting FUAssetImportSetting::GetDefaultExportSetting(const FString& InTargetDir)
  5962. {
  5963. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  5964. FUAssetImportSetting ImportSetting;
  5965. ImportSetting.bExportMode = true;
  5966. ImportSetting.bSkipImportIfAnyDependencyMissing = ExtContentBrowserSetting->bSkipExportIfAnyDependencyMissing;
  5967. #if ECB_FEA_IMPORT_IGNORE_SOFT_ERROR
  5968. ImportSetting.bIgnoreSoftReferencesError = ExtContentBrowserSetting->bExportIgnoreSoftReferencesError;
  5969. #endif
  5970. ImportSetting.bOverwriteExistingFiles = ExtContentBrowserSetting->bExportOverwriteExistingFiles;
  5971. ImportSetting.bRollbackIfFailed = ExtContentBrowserSetting->bRollbackExportIfFailed;
  5972. ImportSetting.bSyncAssetsInContentBrowser = false;
  5973. ImportSetting.bSyncExistingAssets = false;
  5974. ImportSetting.bLoadAssetAfterImport = false;
  5975. ImportSetting.TargetContentDir = InTargetDir;
  5976. ImportSetting.bSandboxMode = false;
  5977. ImportSetting.bFlatMode= false;
  5978. return ImportSetting;
  5979. }
  5980. FUAssetImportSetting FUAssetImportSetting::GetZipExportSetting()
  5981. {
  5982. FUAssetImportSetting ImportSetting;
  5983. ImportSetting.bExportMode = true;
  5984. ImportSetting.bSkipImportIfAnyDependencyMissing = false;
  5985. #if ECB_FEA_IMPORT_IGNORE_SOFT_ERROR
  5986. ImportSetting.bIgnoreSoftReferencesError = true;
  5987. #endif
  5988. ImportSetting.bOverwriteExistingFiles = true;
  5989. ImportSetting.bRollbackIfFailed = false;
  5990. ImportSetting.bSyncAssetsInContentBrowser = false;
  5991. ImportSetting.bSyncExistingAssets = false;
  5992. ImportSetting.bLoadAssetAfterImport = false;
  5993. ImportSetting.bSandboxMode = false;
  5994. ImportSetting.bFlatMode = false;
  5995. return ImportSetting;
  5996. }
  5997. FUAssetImportSetting FUAssetImportSetting::GetSavedImportSetting()
  5998. {
  5999. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  6000. FUAssetImportSetting ImportSetting;
  6001. ImportSetting.bSkipImportIfAnyDependencyMissing = ExtContentBrowserSetting->bSkipImportIfAnyDependencyMissing;
  6002. ImportSetting.bOverwriteExistingFiles = ExtContentBrowserSetting->bImportOverwriteExistingFiles;
  6003. ImportSetting.bRollbackIfFailed = ExtContentBrowserSetting->bRollbackImportIfFailed;
  6004. ImportSetting.bSyncAssetsInContentBrowser = ExtContentBrowserSetting->bImportSyncAssetsInContentBrowser;
  6005. ImportSetting.bSyncExistingAssets = ExtContentBrowserSetting->bImportSyncExistingAssets;
  6006. ImportSetting.bLoadAssetAfterImport = ExtContentBrowserSetting->bLoadAssetAfterImport;
  6007. #if ECB_WIP_IMPORT_ADD_TO_COLLECTION
  6008. ImportSetting.bAddImportedAssetsToCollection = ExtContentBrowserSetting->bAddImportedAssetsToCollection;
  6009. ImportSetting.bUniqueCollectionNameForEachImportSession = ExtContentBrowserSetting->bUniqueCollectionNameForEachImportSession;
  6010. ImportSetting.ImportedUAssetCollectionName = ExtContentBrowserSetting->DefaultImportedUAssetCollectionName;
  6011. #endif
  6012. #if ECB_WIP_IMPORT_TO_PLUGIN_FOLDER
  6013. ImportSetting.bImportToPluginFolder = ExtContentBrowserSetting->bImportToPluginFolder;
  6014. ImportSetting.bWarnBeforeImportToPluginFolder = ExtContentBrowserSetting->bWarnBeforeImportToPluginFolder;
  6015. ImportSetting.ImportToPluginName = ExtContentBrowserSetting->ImportToPluginName;
  6016. #endif
  6017. #if ECB_WIP_IMPORT_FOLDER_COLOR
  6018. ImportSetting.bImportFolderColor = ExtContentBrowserSetting->bImportFolderColor;
  6019. #if ECB_WIP_IMPORT_FOLDER_COLOR_OVERRIDE
  6020. ImportSetting.bOverrideExistingFolderColor = ExtContentBrowserSetting->bOverrideExistingFolderColor;
  6021. #else
  6022. ImportSetting.bOverrideExistingFolderColor = false;
  6023. #endif
  6024. #endif
  6025. #if ECB_FEA_IMPORT_IGNORE_SOFT_ERROR
  6026. ImportSetting.bIgnoreSoftReferencesError = ExtContentBrowserSetting->bImportIgnoreSoftReferencesError;
  6027. #endif
  6028. ImportSetting.ResolveTargetContentDir();
  6029. return ImportSetting;
  6030. }
  6031. FUAssetImportSetting FUAssetImportSetting::GetSandboxImportSetting()
  6032. {
  6033. FUAssetImportSetting ImportSetting;
  6034. {
  6035. ImportSetting.TargetContentDir = FExtAssetData::GetSandboxDir();
  6036. ImportSetting.bSandboxMode = true;
  6037. ImportSetting.bFlatMode = false;
  6038. ImportSetting.bSkipImportIfAnyDependencyMissing = false;
  6039. ImportSetting.bOverwriteExistingFiles = true;
  6040. ImportSetting.bRollbackIfFailed = /*true*/false; // no need to handle rollback for sandbox
  6041. ImportSetting.bSyncAssetsInContentBrowser = true;
  6042. ImportSetting.bSyncExistingAssets = true;
  6043. ImportSetting.bLoadAssetAfterImport = true;
  6044. #if ECB_WIP_IMPORT_ADD_TO_COLLECTION
  6045. ImportSetting.bAddImportedAssetsToCollection = false;
  6046. ImportSetting.bUniqueCollectionNameForEachImportSession = false;
  6047. ImportSetting.ImportedUAssetCollectionName = NAME_None;
  6048. #endif
  6049. #if ECB_WIP_IMPORT_TO_PLUGIN_FOLDER
  6050. ImportSetting.bImportToPluginFolder = false;
  6051. ImportSetting.bWarnBeforeImportToPluginFolder = false;
  6052. ImportSetting.ImportToPluginName = NAME_None;
  6053. #endif
  6054. #if ECB_WIP_IMPORT_FOLDER_COLOR
  6055. ImportSetting.bImportFolderColor = false;
  6056. ImportSetting.bOverrideExistingFolderColor = false;
  6057. #endif
  6058. #if ECB_FEA_IMPORT_IGNORE_SOFT_ERROR
  6059. ImportSetting.bIgnoreSoftReferencesError = true;
  6060. #endif
  6061. }
  6062. return ImportSetting;
  6063. }
  6064. FUAssetImportSetting FUAssetImportSetting::GetSandboxImportForDumpSetting()
  6065. {
  6066. FUAssetImportSetting ImportSetting = GetSandboxImportSetting();
  6067. {
  6068. ImportSetting.bDumpAsset = true;
  6069. ImportSetting.bFlatMode = true;
  6070. ImportSetting.bSyncAssetsInContentBrowser = false;
  6071. ImportSetting.bSyncExistingAssets = false;
  6072. {
  6073. const FString SessionId = FGuid::NewGuid().ToString(EGuidFormats::Digits);
  6074. ImportSetting.TargetContentDir = FPaths::Combine(FExtAssetData::GetSandboxDir(), SessionId);
  6075. }
  6076. }
  6077. return ImportSetting;
  6078. }
  6079. FUAssetImportSetting FUAssetImportSetting::GetFlatModeImportSetting()
  6080. {
  6081. FUAssetImportSetting ImportSetting = GetSavedImportSetting();
  6082. {
  6083. ImportSetting.bFlatMode = true;
  6084. ImportSetting.bLoadAssetAfterImport = true;
  6085. }
  6086. return ImportSetting;
  6087. }
  6088. FUAssetImportSetting FUAssetImportSetting::GetDirectCopyModeImportSetting()
  6089. {
  6090. FUAssetImportSetting ImportSetting = GetSavedImportSetting();
  6091. {
  6092. ImportSetting.bDirectCopyMode = true;
  6093. ImportSetting.bWarnBeforeImportToPluginFolder = false;
  6094. }
  6095. return ImportSetting;
  6096. }
  6097. void FUAssetImportSetting::ResolveTargetContentDir()
  6098. {
  6099. #if ECB_WIP_IMPORT_TO_PLUGIN_FOLDER
  6100. if (!bSandboxMode && bImportToPluginFolder)
  6101. {
  6102. FString ValidPluginContentDir;
  6103. InValidReason.Empty();
  6104. bValid = FExtAssetImporter::IsValidImportToPluginName(ImportToPluginName, &InValidReason, &ValidPluginContentDir);
  6105. if (bValid)
  6106. {
  6107. TargetContentDir = ValidPluginContentDir;
  6108. }
  6109. }
  6110. #endif
  6111. }
  6112. #if 0
  6113. //--------------------------------------------------
  6114. // FUAssetBatchImportSetting Impl
  6115. //
  6116. FUAssetBatchImportSetting::FUAssetBatchImportSetting()
  6117. : FUAssetImportSetting()
  6118. {
  6119. bWarnBeforeImportToPluginFolder = false;
  6120. bSyncAssetsInContentBrowser = false;
  6121. bSyncExistingAssets = false;
  6122. bLoadAssetAfterImport = false;
  6123. bUniqueCollectionNameForEachImportSession = false;
  6124. }
  6125. FUAssetBatchImportSetting::FUAssetBatchImportSetting(const FUAssetImportSetting& BaseSetting)
  6126. : FUAssetImportSetting(BaseSetting)
  6127. {
  6128. }
  6129. #endif
  6130. //--------------------------------------------------
  6131. // FExtAssetRegistryStateCache Impl
  6132. //
  6133. void FExtAssetRegistryStateCache::Reset()
  6134. {
  6135. CachedAssetsByFilePath.Empty();
  6136. CachedUnParsedAssets.Empty();
  6137. CachedDependencyInfoByFilePath.Empty();
  6138. CachedAssetsByFolder.Empty();
  6139. CachedAssetsByClass.Empty();
  6140. CachedAssetsByTag.Empty();
  6141. CachedFilePathsByFolder.Empty();
  6142. CachedSubPaths.Empty();
  6143. CachedEmptyFoldersStatus.Empty();
  6144. CachedFullyParsedFolders.Empty();
  6145. CachedPackageNameToFilePathMap.Empty();
  6146. CachedAssetContentRoots.Empty();
  6147. CachedAssetContentType.Empty();
  6148. CachedAssetContentRootConfigDirs.Empty();
  6149. CachedAssetContentRootHosts.Empty();
  6150. CachedFolderColorIndices.Empty();
  6151. CachedFolderColors.Empty();
  6152. CachedThumbnails.Empty();
  6153. }
  6154. void FExtAssetRegistryStateCache::ClearThumbnailCache()
  6155. {
  6156. CachedThumbnails.Empty();
  6157. }
  6158. void FExtAssetRegistryStateCache::CacheNewAsset(FExtAssetData* InAssetToCache)
  6159. {
  6160. // Add to CachedAssetsByFolder
  6161. {
  6162. const FString Dir = InAssetToCache->GetFolderPath();
  6163. TArray<FExtAssetData*>& CachedAssets = CachedAssetsByFolder.FindOrAdd(*Dir);
  6164. CachedAssets.AddUnique(InAssetToCache);
  6165. }
  6166. // Add asset to CachedAssetsByFilePath
  6167. CachedAssetsByFilePath.Add(InAssetToCache->PackageFilePath, InAssetToCache);
  6168. // PackageName to file path map
  6169. if (InAssetToCache->PackageName != NAME_None)
  6170. {
  6171. CachedPackageNameToFilePathMap.FindOrAdd(InAssetToCache->PackageName) = InAssetToCache->PackageFilePath;
  6172. }
  6173. if (InAssetToCache->IsParsed())
  6174. {
  6175. CacheValidAssetInfo(InAssetToCache);
  6176. }
  6177. else
  6178. {
  6179. CachedUnParsedAssets.Add(InAssetToCache->PackageFilePath, InAssetToCache);
  6180. }
  6181. }
  6182. void FExtAssetRegistryStateCache::ReCacheParsedAsset(FExtAssetData* InAssetToCache)
  6183. {
  6184. if (!InAssetToCache->IsParsed())
  6185. {
  6186. return;
  6187. }
  6188. for (auto It = CachedUnParsedAssets.CreateIterator(); It; ++It)
  6189. {
  6190. if (It->Key == InAssetToCache->PackageFilePath)
  6191. {
  6192. CacheValidAssetInfo(It->Value);
  6193. }
  6194. It.RemoveCurrent();
  6195. }
  6196. }
  6197. void FExtAssetRegistryStateCache::UpdateCacheByRemovedFolders(const TArray<FString>& InRemovedFolders)
  6198. {
  6199. ECB_LOG(Display, TEXT("Before UpdateCache:"));
  6200. PrintStatus();
  6201. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  6202. for (const FString& InRemovedFolder : InRemovedFolders)
  6203. {
  6204. FName FolderName(*InRemovedFolder);
  6205. TSet<FName> AllFoldersToRemove;
  6206. TSet<FName> AllFilesToRemove;
  6207. AllFoldersToRemove.Add(FolderName);
  6208. {
  6209. TSet<FName> AllSubPaths;
  6210. FContentPathUtil::ExpandCachedSubsRecursively(CachedSubPaths, *InRemovedFolder, AllSubPaths);
  6211. AllFoldersToRemove.Append(AllSubPaths);
  6212. }
  6213. // exact match
  6214. {
  6215. for (const FName& FolderPath : AllFoldersToRemove)
  6216. {
  6217. // CachedSubPaths, CachedEmptyFoldersStatus
  6218. CachedSubPaths.Remove(FolderPath);
  6219. CachedEmptyFoldersStatus.Remove(FolderPath);
  6220. CachedFullyParsedFolders.Remove(FolderPath);
  6221. // CachedAssetsByFolder
  6222. if (!ExtContentBrowserSetting->bKeepCachedAssetsWhenRootRemoved)
  6223. CachedAssetsByFolder.Remove(FolderPath);
  6224. // CachedFilePathsByFolder
  6225. if (auto* FilePathsPtr = CachedFilePathsByFolder.Find(FolderPath))
  6226. {
  6227. AllFilesToRemove.Append(*FilePathsPtr);
  6228. }
  6229. CachedFilePathsByFolder.Remove(FolderPath);
  6230. }
  6231. }
  6232. // CachedPackageNameToFilePathMap
  6233. for (auto It = CachedPackageNameToFilePathMap.CreateIterator(); It; ++It)
  6234. {
  6235. if (AllFilesToRemove.Contains(It->Value))
  6236. {
  6237. It.RemoveCurrent();
  6238. }
  6239. }
  6240. // CachedAssetContentRootDirs, CachedAssetContentType
  6241. {
  6242. for (int32 Index = CachedAssetContentRoots.Num() - 1; Index >= 0; --Index)
  6243. {
  6244. FName& CachedContentRoot = CachedAssetContentRoots[Index];
  6245. //if (CachedAssetContentRootDirs[Index].ToString().StartsWith(InRemovedFolder))
  6246. if (FPathsUtil::IsSubOrSamePath(CachedContentRoot.ToString(), InRemovedFolder))
  6247. {
  6248. CachedAssetContentType.Remove(CachedContentRoot);
  6249. CachedAssetContentRootConfigDirs.Remove(CachedContentRoot);
  6250. if (const FName* HostDirPtr = CachedAssetContentRootHosts.FindKey(CachedContentRoot))
  6251. {
  6252. CachedAssetContentRootHosts.Remove(*HostDirPtr);
  6253. }
  6254. if (auto* Folders = CachedFolderColorIndices.Find(CachedContentRoot))
  6255. {
  6256. for (auto& Folder : *Folders)
  6257. {
  6258. CachedFolderColors.Remove(Folder);
  6259. }
  6260. }
  6261. CachedFolderColorIndices.Remove(CachedContentRoot);
  6262. CachedAssetContentRoots.RemoveAt(Index);
  6263. }
  6264. }
  6265. }
  6266. if (ExtContentBrowserSetting->bKeepCachedAssetsWhenRootRemoved)
  6267. continue;
  6268. // CachedAssetsByFilePath, CachedDependencyInfoByFilePath, CachedUnParsedAssets, CachedThumbnails
  6269. for (auto& FilePath : AllFilesToRemove)
  6270. {
  6271. CachedAssetsByFilePath.Remove(FilePath);
  6272. CachedUnParsedAssets.Remove(FilePath);
  6273. CachedDependencyInfoByFilePath.Remove(FilePath);
  6274. #if ECB_WIP_THUMB_CACHE
  6275. CachedThumbnails.Remove(FilePath);
  6276. #endif
  6277. }
  6278. // CachedAssetsByClass
  6279. for (auto It = CachedAssetsByClass.CreateIterator(); It; ++It)
  6280. {
  6281. TArray<FExtAssetData*>& Assets = It->Value;
  6282. int32 NumAssets = It->Value.Num();
  6283. for (int32 Index = NumAssets - 1; Index >= 0; --Index)
  6284. {
  6285. if (!Assets[Index] || AllFilesToRemove.Contains(Assets[Index]->PackageFilePath))
  6286. {
  6287. Assets.RemoveAt(Index);
  6288. }
  6289. }
  6290. if (Assets.Num() == 0)
  6291. {
  6292. It.RemoveCurrent();
  6293. }
  6294. }
  6295. // CachedAssetsByTag
  6296. for (auto It = CachedAssetsByTag.CreateIterator(); It; ++It)
  6297. {
  6298. TArray<FExtAssetData*>& Assets = It->Value;
  6299. int32 NumAssets = It->Value.Num();
  6300. for (int32 Index = NumAssets - 1; Index >= 0; --Index)
  6301. {
  6302. if (!Assets[Index] || AllFilesToRemove.Contains(Assets[Index]->PackageFilePath))
  6303. {
  6304. Assets.RemoveAt(Index);
  6305. }
  6306. }
  6307. if (Assets.Num() == 0)
  6308. {
  6309. It.RemoveCurrent();
  6310. }
  6311. }
  6312. ECB_LOG(Display, TEXT("%d folder, %d files removed from cache of %s"), AllFoldersToRemove.Num(), AllFilesToRemove.Num(), *InRemovedFolder);
  6313. }
  6314. for (const FString& InRemovedFolder : InRemovedFolders)
  6315. {
  6316. for (auto It = CachedSubPaths.CreateIterator(); It; ++It)
  6317. {
  6318. const FName ParentPath = It->Key;
  6319. //if (ParentPath.ToString().StartsWith(InRemovedFolder))
  6320. if (FPathsUtil::IsSubOrSamePath(ParentPath.ToString(), InRemovedFolder))
  6321. {
  6322. It.RemoveCurrent();
  6323. }
  6324. }
  6325. }
  6326. //CollectGarbage(RF_NoFlags);
  6327. ECB_LOG(Display, TEXT("After UpdateCache:"));
  6328. PrintStatus();
  6329. }
  6330. bool FExtAssetRegistryStateCache::PurgeAssets(bool bSilent/* = true*/)
  6331. {
  6332. int32 NumFoldersPurged = 0;
  6333. int32 NumAssetsPurged = 0;
  6334. ECB_LOG(Display, TEXT("Before PurgeAssets:"));
  6335. PrintStatus();
  6336. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  6337. TArray<FString> RootContentPaths;
  6338. FExtContentBrowserSingleton::GetAssetRegistry().QueryRootContentPaths(RootContentPaths);
  6339. TSet<FName> AllFoldersToRemove;
  6340. for (auto It = CachedSubPaths.CreateIterator(); It; ++It)
  6341. {
  6342. const FName ParentPath = It->Key;
  6343. bool bFoundInRoot = false;
  6344. for (auto& RootPath : RootContentPaths)
  6345. {
  6346. if (FPathsUtil::IsSubOrSamePath(ParentPath.ToString(), RootPath))
  6347. {
  6348. bFoundInRoot = true;
  6349. break;
  6350. }
  6351. }
  6352. if (bFoundInRoot)
  6353. {
  6354. continue;
  6355. }
  6356. AllFoldersToRemove.Add(It->Key);
  6357. //AllFoldersToRemove.Append(It->Value);
  6358. TSet<FName>& ToRemoves = It->Value;
  6359. for (auto& ToRemove : ToRemoves)
  6360. {
  6361. TSet<FName> AllSubPaths;
  6362. FContentPathUtil::ExpandCachedSubsRecursively(CachedSubPaths, *ToRemove.ToString(), AllSubPaths);
  6363. AllFoldersToRemove.Append(AllSubPaths);
  6364. }
  6365. }
  6366. for (const FName& FolderPath : AllFoldersToRemove)
  6367. {
  6368. // CachedSubPaths, CachedEmptyFoldersStatus, CachedFullyParsedFolders, CachedFilePathsByFolder
  6369. CachedSubPaths.Remove(FolderPath);
  6370. CachedEmptyFoldersStatus.Remove(FolderPath);
  6371. CachedFullyParsedFolders.Remove(FolderPath);
  6372. CachedFilePathsByFolder.Remove(FolderPath);
  6373. }
  6374. ECB_LOG(Display, TEXT("%d folders removed from cache"), AllFoldersToRemove.Num());
  6375. TSet<FName> AllFilesToRemove;
  6376. // CachedAssetsByFolder
  6377. for (auto It = CachedAssetsByFolder.CreateIterator(); It; ++It)
  6378. {
  6379. const FName ParentPath = It->Key;
  6380. bool bFoundInRoot = false;
  6381. for (auto& RootPath : RootContentPaths)
  6382. {
  6383. if (FPathsUtil::IsSubOrSamePath(ParentPath.ToString(), RootPath))
  6384. {
  6385. bFoundInRoot = true;
  6386. break;
  6387. }
  6388. }
  6389. if (bFoundInRoot)
  6390. {
  6391. continue;
  6392. }
  6393. for (auto& AsssetData : It->Value)
  6394. {
  6395. AllFilesToRemove.Add(AsssetData->PackageFilePath);
  6396. }
  6397. It.RemoveCurrent();
  6398. }
  6399. {
  6400. // CachedAssetsByFilePath, CachedDependencyInfoByFilePath, CachedUnParsedAssets, CachedThumbnails, CachedPackageNameToFilePathMap
  6401. for (auto& FilePath : AllFilesToRemove)
  6402. {
  6403. CachedAssetsByFilePath.Remove(FilePath);
  6404. CachedUnParsedAssets.Remove(FilePath);
  6405. CachedDependencyInfoByFilePath.Remove(FilePath);
  6406. #if ECB_WIP_THUMB_CACHE
  6407. CachedThumbnails.Remove(FilePath);
  6408. #endif
  6409. // CachedPackageNameToFilePathMap
  6410. for (auto It = CachedPackageNameToFilePathMap.CreateIterator(); It; ++It)
  6411. {
  6412. if (AllFilesToRemove.Contains(It->Value))
  6413. {
  6414. It.RemoveCurrent();
  6415. }
  6416. }
  6417. }
  6418. // CachedAssetsByClass
  6419. for (auto It = CachedAssetsByClass.CreateIterator(); It; ++It)
  6420. {
  6421. TArray<FExtAssetData*>& Assets = It->Value;
  6422. int32 NumAssets = It->Value.Num();
  6423. for (int32 Index = NumAssets - 1; Index >= 0; --Index)
  6424. {
  6425. if (!Assets[Index] || AllFilesToRemove.Contains(Assets[Index]->PackageFilePath))
  6426. {
  6427. Assets.RemoveAt(Index);
  6428. }
  6429. }
  6430. if (Assets.Num() == 0)
  6431. {
  6432. It.RemoveCurrent();
  6433. }
  6434. }
  6435. // CachedAssetsByTag
  6436. for (auto It = CachedAssetsByTag.CreateIterator(); It; ++It)
  6437. {
  6438. TArray<FExtAssetData*>& Assets = It->Value;
  6439. int32 NumAssets = It->Value.Num();
  6440. for (int32 Index = NumAssets - 1; Index >= 0; --Index)
  6441. {
  6442. if (!Assets[Index] || AllFilesToRemove.Contains(Assets[Index]->PackageFilePath))
  6443. {
  6444. Assets.RemoveAt(Index);
  6445. }
  6446. }
  6447. if (Assets.Num() == 0)
  6448. {
  6449. It.RemoveCurrent();
  6450. }
  6451. }
  6452. ECB_LOG(Display, TEXT("%d files removed from cache"), AllFilesToRemove.Num());
  6453. }
  6454. //CollectGarbage(RF_NoFlags);
  6455. ECB_LOG(Display, TEXT("After UpdateCache:"));
  6456. PrintStatus();
  6457. NumFoldersPurged = AllFoldersToRemove.Num();
  6458. NumAssetsPurged = AllFilesToRemove.Num();
  6459. if (!bSilent)
  6460. {
  6461. FString Message = FString::Printf(TEXT("%d folders and %d assets data purged from cache."), NumFoldersPurged, NumAssetsPurged);
  6462. ExtContentBrowserUtils::NotifyMessage(Message);
  6463. }
  6464. return NumFoldersPurged > 0 || NumAssetsPurged > 0;
  6465. }
  6466. void FExtAssetRegistryStateCache::PrintStatus() const
  6467. {
  6468. ECB_LOG(Display, TEXT(" FExtAssetRegistryStateCache"));
  6469. ECB_LOG(Display, TEXT(" ================================="));
  6470. ECB_LOG(Display, TEXT(" CachedAssetsByFilePath: %d"), CachedAssetsByFilePath.Num());
  6471. ECB_LOG(Display, TEXT(" CachedUnParsedAssets: %d"), CachedUnParsedAssets.Num());
  6472. ECB_LOG(Display, TEXT(" CachedDependencyInfoByFilePath: %d"), CachedDependencyInfoByFilePath.Num());
  6473. ECB_LOG(Display, TEXT(" CachedAssetsByFolder: %d"), CachedAssetsByFolder.Num());
  6474. ECB_LOG(Display, TEXT(" CachedAssetsByClass: %d"), CachedAssetsByClass.Num());
  6475. ECB_LOG(Display, TEXT(" CachedAssetsByTag: %d"), CachedAssetsByTag.Num());
  6476. ECB_LOG(Display, TEXT(" CachedRootContentPaths: %d"), CachedRootContentPaths.Num());
  6477. ECB_LOG(Display, TEXT(" CachedSubPaths: %d"), CachedSubPaths.Num());
  6478. ECB_LOG(Display, TEXT(" CachedEmptyFolders: %d"), CachedEmptyFoldersStatus.Num());
  6479. ECB_LOG(Display, TEXT(" CachedFullyParsedFolders: %d"), CachedFullyParsedFolders.Num());
  6480. ECB_LOG(Display, TEXT(" CachedPackageNameToFilePathMap: %d"), CachedPackageNameToFilePathMap.Num());
  6481. ECB_LOG(Display, TEXT(" CachedAssetContentRoots: %d"), CachedAssetContentRoots.Num());
  6482. ECB_LOG(Display, TEXT(" CachedAssetContentType: %d"), CachedAssetContentType.Num());
  6483. ECB_LOG(Display, TEXT(" CachedAssetContentRootConfigDir: %d"), CachedAssetContentRootConfigDirs.Num());
  6484. ECB_LOG(Display, TEXT(" CachedAssetContentRootHostDirs: %d"), CachedAssetContentRootHosts.Num());
  6485. ECB_LOG(Display, TEXT(" CachedFolderColors: %d"), CachedFolderColors.Num());
  6486. ECB_LOG(Display, TEXT(" CachedFolderColorIndices: %d"), CachedFolderColorIndices.Num());
  6487. #if ECB_WIP_THUMB_CACHE
  6488. ECB_LOG(Display, TEXT(" CachedThumbnails: %d"), CachedThumbnails.Num());
  6489. #endif
  6490. }
  6491. void FExtAssetRegistryStateCache::CacheValidAssetInfo(FExtAssetData* InAssetToCache)
  6492. {
  6493. if (!InAssetToCache || !InAssetToCache->IsValid())
  6494. {
  6495. return;
  6496. }
  6497. // Add asset to CachedAssetsByClass
  6498. TArray<FExtAssetData*>& ClassAssets = CachedAssetsByClass.FindOrAdd(InAssetToCache->AssetClass);
  6499. ClassAssets.Add(InAssetToCache);
  6500. // Add asset to CachedAssetsByTag
  6501. for (auto TagIt = InAssetToCache->TagsAndValues.CreateConstIterator(); TagIt; ++TagIt)
  6502. {
  6503. FName Key = TagIt.Key();
  6504. TArray<FExtAssetData*>& TagAssets = CachedAssetsByTag.FindOrAdd(Key);
  6505. TagAssets.Add(InAssetToCache);
  6506. }
  6507. #if ECB_WIP_THUMB_CACHE
  6508. if (!CachedThumbnails.Contains(InAssetToCache->PackageFilePath))
  6509. {
  6510. FObjectThumbnail Thumb;
  6511. if (InAssetToCache->LoadThumbnail(Thumb))
  6512. {
  6513. CachedThumbnails.FindOrAdd(InAssetToCache->PackageFilePath) = MoveTemp(Thumb);
  6514. }
  6515. }
  6516. #endif
  6517. }
  6518. ////////////////////////////////////
  6519. // FExtFolderGatherer Impl
  6520. //
  6521. FExtFolderGatherer::FExtFolderGatherer(const FString& InGatheringFolder)
  6522. : GatherTime(0.0)
  6523. , Thread(nullptr)
  6524. , bRequestToStop(false)
  6525. , bRequestToSkipCurrentSearchFolder(false)
  6526. {
  6527. CurrentSearchFolder = TEXT("");
  6528. DirectoriesToSearch.Add(InGatheringFolder);
  6529. Thread = FRunnableThread::Create(this, TEXT("FExtFolderGatherer"), 0, TPri_BelowNormal);
  6530. checkf(Thread, TEXT("Failed to create folder gatherer thread"));
  6531. }
  6532. FExtFolderGatherer::FExtFolderGatherer(const FString& InParentFolder, const FExtFolderGatherPolicy& InGatherPolicy)
  6533. : FExtFolderGatherer(InParentFolder)
  6534. {
  6535. GatherPolicy = InGatherPolicy;
  6536. }
  6537. FExtFolderGatherer::~FExtFolderGatherer()
  6538. {
  6539. }
  6540. bool FExtFolderGatherer::Init()
  6541. {
  6542. return true;
  6543. }
  6544. uint32 FExtFolderGatherer::Run()
  6545. {
  6546. while (!bRequestToStop)
  6547. {
  6548. TArray<FString> LocalDirectoriesToSearch;
  6549. {
  6550. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6551. LocalDirectoriesToSearch = DirectoriesToSearch;
  6552. }
  6553. if (LocalDirectoriesToSearch.Num() > 0)
  6554. {
  6555. double GatherStartTime = FPlatformTime::Seconds();
  6556. //for (const FString& LocalDir : LocalDirectoriesToSearch)
  6557. const FString& LocalDir = LocalDirectoriesToSearch.Last();
  6558. {
  6559. CurrentSearchFolder = LocalDir;
  6560. bRequestToSkipCurrentSearchFolder = false;
  6561. TArray<FName> SubDirs;
  6562. TMap<FName, TArray<FName>> RecursiveSubDirs;
  6563. TMap<FName, TArray<FName>> Pakages;
  6564. const bool bSuccess = GetAllDirectoriesAndPackagesRecursively(*LocalDir, SubDirs, RecursiveSubDirs, Pakages);
  6565. //if (bSuccess)
  6566. {
  6567. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6568. DirectoriesToSearch.Remove(LocalDir);
  6569. RootDirGatherResults.Emplace(*LocalDir, *LocalDir, SubDirs, RecursiveSubDirs, Pakages);
  6570. GatherTime = FPlatformTime::Seconds() - GatherStartTime;
  6571. }
  6572. }
  6573. }
  6574. CurrentSearchFolder = TEXT("");
  6575. // No work to do. Sleep for a little and try again later.
  6576. FPlatformProcess::Sleep(0.1);
  6577. }
  6578. return 0;
  6579. }
  6580. void FExtFolderGatherer::Stop()
  6581. {
  6582. bRequestToStop = true;
  6583. }
  6584. void FExtFolderGatherer::Exit()
  6585. {
  6586. }
  6587. void FExtFolderGatherer::EnsureCompletion()
  6588. {
  6589. {
  6590. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6591. DirectoriesToSearch.Empty();
  6592. }
  6593. Stop();
  6594. if (Thread != nullptr)
  6595. {
  6596. //Thread->WaitForCompletion();
  6597. Thread->Kill(true);
  6598. delete Thread;
  6599. Thread = nullptr;
  6600. }
  6601. }
  6602. void FExtFolderGatherer::AddSearchFolder(const FString& InFolderToSearch, const FExtFolderGatherPolicy* InUpdatedGatherPolicy)
  6603. {
  6604. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6605. if (!DirectoriesToSearch.Contains(InFolderToSearch))
  6606. {
  6607. DirectoriesToSearch.Add(InFolderToSearch);
  6608. }
  6609. if (InUpdatedGatherPolicy != NULL)
  6610. {
  6611. GatherPolicy.UpdateWith(*InUpdatedGatherPolicy);
  6612. }
  6613. }
  6614. void FExtFolderGatherer::StopSearchFolder(const FString& InFolderToStopSearch)
  6615. {
  6616. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6617. if (DirectoriesToSearch.Contains(InFolderToStopSearch))
  6618. {
  6619. DirectoriesToSearch.Remove(InFolderToStopSearch);
  6620. }
  6621. if (CurrentSearchFolder.Equals(InFolderToStopSearch))
  6622. {
  6623. bRequestToSkipCurrentSearchFolder = true;
  6624. }
  6625. }
  6626. bool FExtFolderGatherer::GetAndTrimGatherResult(TArray<FExtFolderGatherResult>& OutGatherResults, double& OutGatherTime, TArray<FExtFolderGatherResult>& OutSubDirGatherResults, TArray<FExtAssetContentRootGatherResult>& OutAssetContentRootGatherResults)
  6627. {
  6628. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6629. bool bMainGathered = RootDirGatherResults.Num() > 0;
  6630. if (bMainGathered)
  6631. {
  6632. OutGatherResults.Append(MoveTemp(RootDirGatherResults));
  6633. RootDirGatherResults.Reset();
  6634. OutGatherTime = GatherTime;
  6635. GatherTime = 0.0;
  6636. }
  6637. else
  6638. {
  6639. if (bool bSubDirGathered = SubDirGatherResults.Num() > 0)
  6640. {
  6641. OutSubDirGatherResults.Append(MoveTemp(SubDirGatherResults));
  6642. SubDirGatherResults.Reset();
  6643. }
  6644. if (bool bAssetContentRootGathered = AssetContentRootGatherResults.Num() > 0)
  6645. {
  6646. OutAssetContentRootGatherResults.Append(MoveTemp(AssetContentRootGatherResults));
  6647. AssetContentRootGatherResults.Reset();
  6648. }
  6649. }
  6650. return bMainGathered;
  6651. }
  6652. void FExtFolderGatherer::GetDirectoriesAndPackages(const FName& InBaseDirectory, TArray<FName>& OutDirList, TArray<FName>& OutFilePaths)
  6653. {
  6654. if (IFileManager::Get().DirectoryExists(*InBaseDirectory.ToString()))
  6655. {
  6656. FExtFolderGatherPolicy::FFolderVisitor& FolderVisitor = GatherPolicy.GetResetedFolderVisitor();
  6657. IFileManager::Get().IterateDirectory(*InBaseDirectory.ToString(), FolderVisitor);
  6658. OutDirList = FolderVisitor.Directories;
  6659. OutFilePaths = FolderVisitor.PackageFiles;
  6660. }
  6661. }
  6662. bool FExtFolderGatherer::GetAllDirectoriesAndPackagesRecursively(const FName& InBaseDirectory, TArray<FName>& OutSubPaths, TMap<FName, TArray<FName>>& OutRecusriveSubPaths, TMap<FName, TArray<FName>>& OutFilePaths)
  6663. {
  6664. double GetAllStartTime = FPlatformTime::Seconds();
  6665. if (IFileManager::Get().DirectoryExists(*InBaseDirectory.ToString()))
  6666. {
  6667. TMap<FName, FPathTree> PathTrees;
  6668. GetChildDirectoriesAndPackages(InBaseDirectory, /*Parent=*/ NAME_None, { InBaseDirectory }, OutSubPaths, OutRecusriveSubPaths, OutFilePaths, PathTrees);
  6669. }
  6670. FString SkipOrCompleted = bRequestToSkipCurrentSearchFolder ? TEXT("skipped") : TEXT("completed");
  6671. ECB_LOG(Display, TEXT("GetAllDirectoriesAndPackagesRecursively %s in %0.4f seconds"), *SkipOrCompleted, FPlatformTime::Seconds() - GetAllStartTime);
  6672. return !bRequestToSkipCurrentSearchFolder;
  6673. }
  6674. void FExtFolderGatherer::GetChildDirectoriesAndPackages(const FName& InRoot, const FName& InParent, const TArray<FName>& InBaseDirs, TArray<FName>& OutSubPaths, TMap<FName, TArray<FName>>& OutRecusriveSubPaths, TMap<FName, TArray<FName>>& OutputFilePaths, TMap<FName, FPathTree>& OutputPathTrees, bool bFoundAssetContentRoot)
  6675. {
  6676. if (bRequestToStop)
  6677. {
  6678. return;
  6679. }
  6680. if (bRequestToSkipCurrentSearchFolder)
  6681. {
  6682. return;
  6683. }
  6684. const bool bIsRoot = InParent == NAME_None;
  6685. for (const auto& BaseDir : InBaseDirs)
  6686. {
  6687. if (bRequestToSkipCurrentSearchFolder)
  6688. {
  6689. return;
  6690. }
  6691. TArray<FName> ChildDirs;
  6692. TArray<FName> ChildPackages;
  6693. GetDirectoriesAndPackages(BaseDir, ChildDirs, ChildPackages);
  6694. const bool bSortAlphabetical = true;
  6695. if (bSortAlphabetical)
  6696. {
  6697. ChildDirs.Sort([](const FName& InA, const FName& InB) { return InA.LexicalLess(InB); });
  6698. }
  6699. if (bIsRoot)
  6700. {
  6701. OutSubPaths = ChildDirs;
  6702. }
  6703. #if ECB_WIP // Gather Empty Folder
  6704. {
  6705. bool bPotentialEmptyFolder = ChildPackages.Num() == 0;
  6706. FPathTree BasePath(InParent, BaseDir, bPotentialEmptyFolder);
  6707. if (!bPotentialEmptyFolder)
  6708. {
  6709. BasePath.MarkPathTreeNotEmpty(OutputPathTrees);
  6710. }
  6711. OutputPathTrees.Add(BaseDir, BasePath);
  6712. }
  6713. #endif
  6714. if (!bFoundAssetContentRoot)
  6715. {
  6716. FString AssetContentRoot = BaseDir.ToString();
  6717. if (AssetContentRoot.EndsWith(TEXT("/Content")))
  6718. {
  6719. FExtAssetData::EContentType ContentType = FExtAssetData::EContentType::Unknown;
  6720. FString VaultCacheHost = AssetContentRoot / TEXT("../..");
  6721. FPaths::CollapseRelativeDirectories(VaultCacheHost);
  6722. FString PluginOrProjectHost = AssetContentRoot / TEXT("..");
  6723. FPaths::CollapseRelativeDirectories(PluginOrProjectHost);
  6724. FString AssetContentRootHost = PluginOrProjectHost;
  6725. if (FExtContentDirFinder::FindWithFolder(VaultCacheHost, TEXT("manifest"), /*bExtension*/ false))
  6726. {
  6727. ContentType = FExtAssetData::EContentType::VaultCache;
  6728. AssetContentRootHost = VaultCacheHost;
  6729. bFoundAssetContentRoot = true;
  6730. }
  6731. else if (FExtContentDirFinder::FindWithFolder(PluginOrProjectHost, TEXT(".uplugin"), /*bExtension*/ true))
  6732. {
  6733. ContentType = FExtAssetData::EContentType::Plugin;
  6734. bFoundAssetContentRoot = true;
  6735. }
  6736. else if (FExtContentDirFinder::FindWithFolder(PluginOrProjectHost, TEXT(".uproject"), /*bExtension*/ true))
  6737. {
  6738. ContentType = FExtAssetData::EContentType::Project;
  6739. bFoundAssetContentRoot = true;
  6740. }
  6741. if (bFoundAssetContentRoot)
  6742. {
  6743. FString ContentRootConfigDir = FExtConfigUtil::GetConfigDirByContentRoot(AssetContentRoot);
  6744. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6745. AssetContentRootGatherResults.Emplace(/*AssetContentRootDir*/BaseDir, /*AssetContentRootParentDir*/ *AssetContentRootHost, /*AssetContentRootConfigDir*/*ContentRootConfigDir, /*AssetContentType*/ContentType);
  6746. }
  6747. }
  6748. }
  6749. //TArray<FName> RecursiveChildDirs;
  6750. OutRecusriveSubPaths.FindOrAdd(BaseDir).Append(ChildDirs);
  6751. //OutputPathList.Append(ChildDirs);
  6752. OutputFilePaths.FindOrAdd(BaseDir) = ChildPackages;
  6753. GetChildDirectoriesAndPackages(InRoot, /*InParent*/ BaseDir, /*InBaseDirs*/ChildDirs, OutSubPaths, /*OutputPathList*/OutRecusriveSubPaths, OutputFilePaths, OutputPathTrees, bFoundAssetContentRoot);
  6754. //OutRecusriveSubPaths.Append(RecursiveChildDirs);
  6755. if (!bIsRoot && ChildDirs.Num() > 0) // root already covered by main gather result
  6756. {
  6757. TMap<FName, TArray<FName>> MyFilePaths;
  6758. MyFilePaths.Add(BaseDir, OutputFilePaths[BaseDir]);
  6759. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6760. SubDirGatherResults.Emplace(InRoot, BaseDir, ChildDirs, OutRecusriveSubPaths, MyFilePaths);
  6761. }
  6762. }
  6763. }
  6764. ////////////////////////////////////
  6765. // FExtAssetGatherer Impl
  6766. //
  6767. void FExtAssetGatherResult::AddReuslt(const FName& InFilePath, const FExtAssetData& InParsedAsset)
  6768. {
  6769. if (InParsedAsset.IsParsed())
  6770. {
  6771. ParsedAssets.Emplace(InFilePath, InParsedAsset);
  6772. }
  6773. }
  6774. void FExtAssetGatherResult::Reset()
  6775. {
  6776. ParsedAssets.Reset();
  6777. }
  6778. bool FExtAssetGatherResult::HasResult() const
  6779. {
  6780. return ParsedAssets.Num() > 0;
  6781. }
  6782. FExtAssetGatherer::FExtAssetGatherer()
  6783. :GatherStartTime(0.0)
  6784. , GatherTime(0.0)
  6785. , Thread(nullptr)
  6786. , bRequestToStop(false)
  6787. {
  6788. Thread = FRunnableThread::Create(this, TEXT("FExtAssetGatherer"), 0, TPri_BelowNormal);
  6789. checkf(Thread, TEXT("Failed to create asset gatherer thread"));
  6790. }
  6791. FExtAssetGatherer::~FExtAssetGatherer()
  6792. {
  6793. }
  6794. bool FExtAssetGatherer::Init()
  6795. {
  6796. return true;
  6797. }
  6798. uint32 FExtAssetGatherer::Run()
  6799. {
  6800. while (!bRequestToStop)
  6801. {
  6802. int32 NumLeftToGather = 0;
  6803. FName LocalFilePathToGather;
  6804. {
  6805. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6806. NumLeftToGather = GatherBatch.Num();
  6807. if (NumLeftToGather > 0)
  6808. {
  6809. LocalFilePathToGather = GatherBatch.Pop(/*bAllowShrinking*/ false);
  6810. }
  6811. }
  6812. if (NumLeftToGather > 0)
  6813. {
  6814. FExtAssetData LocalAssetDataToParse;
  6815. if (DoGatherAssetByFilePath(LocalFilePathToGather, LocalAssetDataToParse))
  6816. {
  6817. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6818. GatherResult.AddReuslt( LocalFilePathToGather, LocalAssetDataToParse);
  6819. }
  6820. }
  6821. NumLeftToGather = GatherBatch.Num();
  6822. if (NumLeftToGather == 0)
  6823. {
  6824. GatherTime = FPlatformTime::Seconds() - GatherStartTime;
  6825. // No work to do. Sleep for a little and try again later.
  6826. FPlatformProcess::Sleep(0.1);
  6827. }
  6828. }
  6829. return 0;
  6830. }
  6831. void FExtAssetGatherer::Stop()
  6832. {
  6833. bRequestToStop = true;
  6834. }
  6835. void FExtAssetGatherer::Exit()
  6836. {
  6837. }
  6838. void FExtAssetGatherer::EnsureCompletion()
  6839. {
  6840. {
  6841. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6842. GatherBatch.Empty();
  6843. }
  6844. Stop();
  6845. if (Thread != nullptr)
  6846. {
  6847. //Thread->WaitForCompletion();
  6848. Thread->Kill(true);
  6849. delete Thread;
  6850. Thread = nullptr;
  6851. }
  6852. }
  6853. void FExtAssetGatherer::AddBatchToGather(const TArray<FName>& InBatch)
  6854. {
  6855. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6856. GatherBatch = InBatch;
  6857. GatherResult.Reset();
  6858. GatherStartTime = FPlatformTime::Seconds();
  6859. }
  6860. void FExtAssetGatherer::CancelGather()
  6861. {
  6862. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6863. GatherBatch.Empty();
  6864. }
  6865. bool FExtAssetGatherer::GetAndTrimGatherResult(FExtAssetGatherResult& OutGatherResult, double& OutGatherTime, int32& OutLeft)
  6866. {
  6867. FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
  6868. bool bHasAnyGathered = GatherResult.HasResult();
  6869. if (bHasAnyGathered)
  6870. {
  6871. OutGatherResult = GatherResult;
  6872. GatherResult.Reset();
  6873. }
  6874. OutLeft = GatherBatch.Num();
  6875. bool bBatchDone = OutLeft == 0;;
  6876. if (bBatchDone)
  6877. {
  6878. OutGatherTime = GatherTime;
  6879. GatherTime = 0.0;
  6880. }
  6881. return bHasAnyGathered;
  6882. }
  6883. bool FExtAssetGatherer::DoGatherAssetByFilePath(const FName& InFilePath, FExtAssetData& OutExtAssetData) const
  6884. {
  6885. OutExtAssetData = FExtAssetData(InFilePath.ToString(), /*bDelayParse*/false);
  6886. return OutExtAssetData.IsParsed();
  6887. }
  6888. /////////////////////////////////////////////////////
  6889. // FExtAssetCounter
  6890. //
  6891. int32 FExtAssetCounter::CountAssetsByFolder(const FString& InFolder, FExtAssetCountInfo& OutCountInfo)
  6892. {
  6893. FExtAssetRegistry& ExtAssetRegistry = FExtContentBrowserSingleton::GetAssetRegistry();
  6894. FName FolderName(*InFolder);
  6895. if (const TArray<FExtAssetData*>* AssetsByFolder = ExtAssetRegistry.GetOrCacheAssetsByFolder(FolderName, /*bDealyParse*/true).Find(FolderName))
  6896. {
  6897. for (const FExtAssetData* AssetData : *AssetsByFolder)
  6898. {
  6899. if (AssetData == nullptr)
  6900. {
  6901. continue;
  6902. }
  6903. ++OutCountInfo.TotalAssetsIncludeUMap;
  6904. FString AssetFile = AssetData->PackageFilePath.ToString();
  6905. if (AssetData->IsValid())
  6906. {
  6907. OutCountInfo.TotalFileSizes += AssetData->FileSize;
  6908. }
  6909. else
  6910. {
  6911. ++OutCountInfo.TotalInvalid;
  6912. int32 FileSize = IFileManager::Get().FileSize(*AssetFile);
  6913. if (FileSize != INDEX_NONE)
  6914. {
  6915. OutCountInfo.TotalFileSizes += FileSize;
  6916. }
  6917. }
  6918. FString Extenstion = FPaths::GetExtension(AssetFile, /*bIncludeDot*/true);
  6919. if (Extenstion.Equals(FExtAssetSupport::AssetPackageExtension, ESearchCase::IgnoreCase))
  6920. {
  6921. ++OutCountInfo.TotalUAsset;
  6922. }
  6923. else if(Extenstion.Equals(FExtAssetSupport::MapPackageExtension, ESearchCase::IgnoreCase))
  6924. {
  6925. ++OutCountInfo.TotalUMap;
  6926. }
  6927. }
  6928. return OutCountInfo.TotalAssetsIncludeUMap;
  6929. }
  6930. return 0;
  6931. }
  6932. ////////////////////////////////////////////////
  6933. // FExtContentDirFinder
  6934. //
  6935. namespace FExtContentDirFinderHelpers
  6936. {
  6937. struct FFileVisitor : public IPlatformFile::FDirectoryVisitor
  6938. {
  6939. FFileVisitor(const TCHAR* InMatch, bool bInMatchExtension)
  6940. : Match(InMatch)
  6941. , bMatchExtension(bInMatchExtension)
  6942. , bFound(false)
  6943. {
  6944. }
  6945. virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
  6946. {
  6947. if (!bIsDirectory)
  6948. {
  6949. FString FileName = FPaths::GetCleanFilename(FilenameOrDirectory);
  6950. bFound = bMatchExtension ? FileName.EndsWith(*Match) : FileName.Equals(*Match, ESearchCase::CaseSensitive);
  6951. if (bFound)
  6952. {
  6953. return false;
  6954. }
  6955. }
  6956. return true;
  6957. }
  6958. FString Match;
  6959. bool bMatchExtension;
  6960. bool bFound;
  6961. };
  6962. struct FFolderVisitor : public IPlatformFile::FDirectoryVisitor
  6963. {
  6964. FFolderVisitor(const TCHAR* InMatch)
  6965. : Match(InMatch)
  6966. , bFound(false)
  6967. {
  6968. FPaths::NormalizeDirectoryName(Match);
  6969. }
  6970. virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override
  6971. {
  6972. if (bIsDirectory)
  6973. {
  6974. FString NormalizedDir(FilenameOrDirectory);
  6975. FPaths::NormalizeDirectoryName(NormalizedDir);
  6976. bFound = Match.Equals(*Match, ESearchCase::CaseSensitive);
  6977. if (bFound)
  6978. {
  6979. return false;
  6980. }
  6981. }
  6982. return true;
  6983. }
  6984. FString Match;
  6985. bool bFound;
  6986. };
  6987. struct FFileFinder
  6988. {
  6989. static bool FindFile(const FString& InputFilePath, FFileVisitor& InputFileVisitor, const TCHAR* InputFind, FString& OutputContentPath, FString& OutputRelativePath)
  6990. {
  6991. bool bFound = false;
  6992. FString ContentPath;
  6993. int32 FindIndex = InputFilePath.Find(InputFind, ESearchCase::CaseSensitive, ESearchDir::FromEnd, InputFilePath.Len());
  6994. for (; FindIndex != INDEX_NONE; FindIndex = InputFilePath.Find(InputFind, ESearchCase::CaseSensitive, ESearchDir::FromEnd, FindIndex))
  6995. {
  6996. ContentPath = InputFilePath.Mid(0, FindIndex);
  6997. if (IFileManager::Get().DirectoryExists(*ContentPath))
  6998. {
  6999. IFileManager::Get().IterateDirectory(*ContentPath, InputFileVisitor);
  7000. bFound = InputFileVisitor.bFound;
  7001. if (bFound)
  7002. {
  7003. ContentPath = FPaths::Combine(ContentPath, InputFind);
  7004. OutputContentPath = ContentPath;
  7005. OutputRelativePath = InputFilePath.Mid(FCString::Strlen(*ContentPath) + 1);
  7006. break;
  7007. }
  7008. }
  7009. }
  7010. return bFound;
  7011. }
  7012. static bool FindFolder(const FString& InputFilePath, FFolderVisitor& InputFolderVisitor, const TCHAR* InputFind, FString& OutputContentPath, FString& OutputRelativePath)
  7013. {
  7014. bool bFound = false;
  7015. FString ContentPath;
  7016. int32 FindIndex = InputFilePath.Find(InputFind, ESearchCase::CaseSensitive, ESearchDir::FromEnd, InputFilePath.Len());
  7017. for (; FindIndex != INDEX_NONE; FindIndex = InputFilePath.Find(InputFind, ESearchCase::CaseSensitive, ESearchDir::FromEnd, FindIndex))
  7018. {
  7019. ContentPath = InputFilePath.Mid(0, FindIndex);
  7020. if (IFileManager::Get().DirectoryExists(*ContentPath))
  7021. {
  7022. IFileManager::Get().IterateDirectory(*ContentPath, InputFolderVisitor);
  7023. bFound = InputFolderVisitor.bFound;
  7024. if (bFound)
  7025. {
  7026. ContentPath = FPaths::Combine(ContentPath, InputFind);
  7027. OutputContentPath = ContentPath;
  7028. OutputRelativePath = InputFilePath.Mid(FCString::Strlen(*ContentPath) + 1);
  7029. break;
  7030. }
  7031. }
  7032. }
  7033. return bFound;
  7034. }
  7035. static bool FindFolder(const FString& InputFolder, FFileVisitor& InputFileVisitor)
  7036. {
  7037. bool bFound = false;
  7038. if (IFileManager::Get().DirectoryExists(*InputFolder))
  7039. {
  7040. IFileManager::Get().IterateDirectory(*InputFolder, InputFileVisitor);
  7041. bFound = InputFileVisitor.bFound;
  7042. }
  7043. return bFound;
  7044. }
  7045. };
  7046. };
  7047. bool FExtContentDirFinder::FindWithFile(const FString& InUAssetFilePath, const FString& InFileToFind, bool bExtension, const FString& InContentFolderPattern, FString& OutContentRootPath, FString& OutRelativePath)
  7048. {
  7049. FExtContentDirFinderHelpers::FFileVisitor FileVisitor(*InFileToFind, /*bExtension*/ bExtension);
  7050. const bool bFoundRootPath = FExtContentDirFinderHelpers::FFileFinder::FindFile(InUAssetFilePath, FileVisitor, *InContentFolderPattern, OutContentRootPath, OutRelativePath);
  7051. return bFoundRootPath;
  7052. }
  7053. bool FExtContentDirFinder::FindFolder(const FString& InUAssetFilePath, const FString& InContentFolderPattern, FString& OutContentRootPath, FString& OutRelativePath)
  7054. {
  7055. FExtContentDirFinderHelpers::FFolderVisitor FolderVisitor(*InContentFolderPattern);
  7056. const bool bFoundRootPath = FExtContentDirFinderHelpers::FFileFinder::FindFolder(InUAssetFilePath, FolderVisitor, *InContentFolderPattern, OutContentRootPath, OutRelativePath);
  7057. return bFoundRootPath;
  7058. }
  7059. bool FExtContentDirFinder::FindWithFolder(const FString& InFolderPath, const FString& InFileToFind, bool bExtension)
  7060. {
  7061. FExtContentDirFinderHelpers::FFileVisitor FileVisitor(*InFileToFind, /*bExtension*/ bExtension);
  7062. const bool bFoundRootPath = FExtContentDirFinderHelpers::FFileFinder::FindFolder(InFolderPath, FileVisitor);
  7063. return bFoundRootPath;
  7064. }
  7065. ////////////////////////////////////////////////
  7066. // FAssetDataUtil
  7067. //
  7068. void FAssetDataUtil::PlaceToCurrentLevelViewport(const TArray<FAssetData>& InAssets)
  7069. {
  7070. FLevelEditorViewportClient* ViewportClient = GCurrentLevelEditingViewportClient;
  7071. if (InAssets.Num() < 1 && !ViewportClient)
  7072. {
  7073. return;
  7074. }
  7075. TArray< UObject* > Assets;
  7076. for (int Index = 0; Index < InAssets.Num(); Index++)
  7077. {
  7078. const FAssetData& AssetData = InAssets[Index];
  7079. if (AssetData.IsValid())
  7080. {
  7081. if (UObject* Asset = AssetData.GetAsset())
  7082. {
  7083. Assets.Add(Asset);
  7084. }
  7085. }
  7086. }
  7087. TArray<AActor*> OutNewActors;
  7088. const bool bCreateDropPreview = false;
  7089. const bool SelectActor = false;
  7090. const FViewport* const Viewport = ViewportClient->Viewport;
  7091. bool AllAssetsCanBeDropped = true;
  7092. // Determine if we can drop the assets
  7093. for (auto AssetIt = Assets.CreateConstIterator(); AssetIt; ++AssetIt)
  7094. {
  7095. UObject* Asset = *AssetIt;
  7096. FDropQuery DropResult = ViewportClient->CanDropObjectsAtCoordinates(Viewport->GetMouseX(), Viewport->GetMouseY(), FAssetData(Asset));
  7097. if (!DropResult.bCanDrop)
  7098. {
  7099. // At least one of the assets can't be dropped.
  7100. ViewportClient->DestroyDropPreviewActors();
  7101. AllAssetsCanBeDropped = false;
  7102. }
  7103. }
  7104. if (AllAssetsCanBeDropped)
  7105. {
  7106. ViewportClient->DropObjectsAtCoordinates(Viewport->GetMouseX(), Viewport->GetMouseY(), Assets, OutNewActors, /*bOnlyDropOnTarget*/false, bCreateDropPreview, SelectActor);
  7107. }
  7108. }
  7109. void FAssetDataUtil::ExportAssetsWithDialog(const TArray<FAssetData>& InAssets)
  7110. {
  7111. struct Local
  7112. {
  7113. static void GatherAssets(const TArray<FAssetData>& InputAssets, TArray<UObject*>& OutObjects, bool bInputSkipRedirectors)
  7114. {
  7115. for (int32 AssetIdx = 0; AssetIdx < InputAssets.Num(); ++AssetIdx)
  7116. {
  7117. if (bInputSkipRedirectors && (InputAssets[AssetIdx].AssetClassPath.GetPackageName() == UObjectRedirector::StaticClass()->GetFName()))
  7118. {
  7119. // Don't operate on Redirectors
  7120. continue;
  7121. }
  7122. UObject* Object = InputAssets[AssetIdx].GetAsset();
  7123. if (Object)
  7124. {
  7125. OutObjects.Add(Object);
  7126. }
  7127. }
  7128. }
  7129. };
  7130. TArray<UObject*> ObjectsToExport;
  7131. const bool bSkipRedirectors = true;
  7132. Local::GatherAssets(InAssets, ObjectsToExport, bSkipRedirectors);
  7133. if (ObjectsToExport.Num() > 0)
  7134. {
  7135. FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
  7136. AssetToolsModule.Get().ExportAssetsWithDialog(ObjectsToExport, /*bPromptForIndividualFilenames*/true);
  7137. const bool bKillAfterExport = true;
  7138. if (bKillAfterExport)
  7139. {
  7140. for (UObject* Asset : ObjectsToExport)
  7141. {
  7142. if (IsValid(Asset))
  7143. {
  7144. Asset->MarkAsGarbage();
  7145. }
  7146. }
  7147. }
  7148. }
  7149. }
  7150. ///////////////////////////////
  7151. // Serializer
  7152. constexpr uint32 ExtAssetRegistryNumberedNameBit = 0x80000000;
  7153. bool FExtAssetRegistryVersion::SerializeVersion(FArchive& Ar, Type& Version)
  7154. {
  7155. FGuid Guid = FExtAssetRegistryVersion::GUID;
  7156. if (Ar.IsLoading())
  7157. {
  7158. Version = FExtAssetRegistryVersion::BeforeCustomVersionWasAdded;
  7159. }
  7160. Ar << Guid;
  7161. if (Ar.IsError())
  7162. {
  7163. return false;
  7164. }
  7165. if (Guid == FExtAssetRegistryVersion::GUID)
  7166. {
  7167. int32 VersionInt = Version;
  7168. Ar << VersionInt;
  7169. Version = (FExtAssetRegistryVersion::Type)VersionInt;
  7170. Ar.SetCustomVersion(Guid, VersionInt, TEXT("ExtAssetRegistry"));
  7171. }
  7172. else
  7173. {
  7174. return false;
  7175. }
  7176. return !Ar.IsError();
  7177. }
  7178. //////////////////////////////////////////////////////////////
  7179. FAssetDataTagMapSharedView LoadTags(class FExtAssetRegistryReader& Reader);
  7180. class FExtAssetRegistryReader : public FArchiveProxy
  7181. {
  7182. public:
  7183. /// @param NumWorkers > 0 for parallel loading
  7184. FExtAssetRegistryReader(FArchive& Inner)
  7185. : FArchiveProxy(Inner)
  7186. {
  7187. check(IsLoading());
  7188. Names = LoadNameBatch(Inner);
  7189. Tags = FixedTagPrivate::LoadStore(*this);
  7190. }
  7191. ~FExtAssetRegistryReader() {}
  7192. virtual FArchive& operator<<(FName& Out) override
  7193. {
  7194. checkf(Names.Num() > 0, TEXT("Attempted to load FName before name batch loading has finished"));
  7195. uint32 Index = 0;
  7196. uint32 Number = NAME_NO_NUMBER_INTERNAL;
  7197. *this << Index;
  7198. if (Index & ExtAssetRegistryNumberedNameBit)
  7199. {
  7200. Index -= ExtAssetRegistryNumberedNameBit;
  7201. *this << Number;
  7202. }
  7203. Out = Names[Index].ToName(Number);
  7204. return *this;
  7205. }
  7206. void SerializeTagsAndBundles(FExtAssetData& Out)
  7207. {
  7208. Out.TagsAndValues = LoadTags(*this);
  7209. }
  7210. private:
  7211. TArray<FDisplayNameEntryId> Names;
  7212. TRefCountPtr<const FixedTagPrivate::FStore> Tags;
  7213. friend FAssetDataTagMapSharedView LoadTags(FExtAssetRegistryReader& Reader);
  7214. };
  7215. FAssetDataTagMapSharedView LoadTags(FExtAssetRegistryReader& Reader)
  7216. {
  7217. uint64 MapHandle;
  7218. Reader << MapHandle;
  7219. return FAssetDataTagMapSharedView(FixedTagPrivate::FPartialMapHandle::FromInt(MapHandle).MakeFullHandle(Reader.Tags->Index));
  7220. }
  7221. class FExtAssetRegistryWriterBase
  7222. {
  7223. protected:
  7224. FLargeMemoryWriter MemWriter;
  7225. };
  7226. /**
  7227. * Indexes FName and tag maps and serializes out deduplicated indices instead.
  7228. *
  7229. * Unlike previous FNameTableArchiveWriter:
  7230. * - Name data stored as name batches, which is faster
  7231. * - Name batch is written as a header instead of footer for faster seek-free loading
  7232. * - Numberless FNames are serialized as a single 32-bit int
  7233. * - Deduplicates all tag values, not just names
  7234. *
  7235. * Use in conjunction with FNameBatchReader.
  7236. *
  7237. * Data is written to inner archive in destructor to achieve seek-free loading.
  7238. */
  7239. class FExtAssetRegistryWriter : public FExtAssetRegistryWriterBase, public FArchiveProxy
  7240. {
  7241. public:
  7242. FExtAssetRegistryWriter(FArchive& Out)
  7243. : FArchiveProxy(MemWriter)
  7244. , Tags(FixedTagPrivate::FOptions())
  7245. , TargetAr(Out)
  7246. {
  7247. check(!IsLoading());
  7248. }
  7249. static TArray<FDisplayNameEntryId> FlattenIndex(const TMap<FDisplayNameEntryId, uint32>& Names)
  7250. {
  7251. TArray<FDisplayNameEntryId> Out;
  7252. Out.SetNumZeroed(Names.Num());
  7253. for (TPair<FDisplayNameEntryId, uint32> Pair : Names)
  7254. {
  7255. Out[Pair.Value] = Pair.Key;
  7256. }
  7257. return Out;
  7258. }
  7259. ~FExtAssetRegistryWriter()
  7260. {
  7261. // Save store data and collect FNames
  7262. int32 BodySize = MemWriter.TotalSize();
  7263. SaveStore(Tags.Finalize(), *this);
  7264. // Save in load-friendly order - names, store then body / tag maps
  7265. #if ECB_WIP_CACHEDB_LOADNAMEBATCH
  7266. SaveNameBatch(FlattenIndex(Names), TargetAr);
  7267. #endif
  7268. TargetAr.Serialize(MemWriter.GetData() + BodySize, MemWriter.TotalSize() - BodySize);
  7269. TargetAr.Serialize(MemWriter.GetData(), BodySize);
  7270. }
  7271. virtual FArchive& operator<<(FName& Value) override
  7272. {
  7273. FDisplayNameEntryId EntryId(Value);
  7274. uint32 Index = Names.FindOrAdd(EntryId, Names.Num());
  7275. check((Index & ExtAssetRegistryNumberedNameBit) == 0);
  7276. if (Value.GetNumber() != NAME_NO_NUMBER_INTERNAL)
  7277. {
  7278. Index |= ExtAssetRegistryNumberedNameBit;
  7279. uint32 Number = Value.GetNumber();
  7280. return *this << Index << Number;
  7281. }
  7282. return *this << Index;
  7283. }
  7284. void SaveTags(FExtAssetRegistryWriter& Writer, const FAssetDataTagMapSharedView& Map)
  7285. {
  7286. uint64 MapHandle = Writer.Tags.AddTagMap(Map).ToInt();
  7287. Writer << MapHandle;
  7288. }
  7289. void SerializeTagsAndBundles(const FExtAssetData& Out)
  7290. {
  7291. SaveTags(*this, Out.TagsAndValues);
  7292. }
  7293. private:
  7294. TMap<FDisplayNameEntryId, uint32> Names;
  7295. FixedTagPrivate::FStoreBuilder Tags;
  7296. FArchive& TargetAr;
  7297. friend void SaveTags(FExtAssetRegistryWriter& Writer, const FAssetDataTagMapSharedView& Map);
  7298. };
  7299. //////////////////////////////////////////////////////////////
  7300. #if ECB_WIP_CACHEDB
  7301. template<class Archive>
  7302. void FExtAssetData::SerializeForCache(Archive&& Ar)
  7303. {
  7304. Ar << PackageFilePath;
  7305. Ar << PackageName;
  7306. Ar << PackagePath;
  7307. Ar << ObjectPath;
  7308. Ar << AssetName;
  7309. Ar << AssetClass;
  7310. //FAssetData AssetData; (Dummy data for FAssetData based interface)
  7311. Ar << AssetContentRoot;
  7312. Ar << AssetRelativePath;
  7313. Ar << AssetContentType; // EContentType
  7314. //Ar << AssetData.TagsAndValues; // FAssetDataTagMapSharedView
  7315. Ar.SerializeTagsAndBundles(*this);
  7316. Ar << HardDependentPackages; // TSet<FName>
  7317. Ar << SoftReferencesList; // TSet<FName>
  7318. Ar << FileVersionUE4;
  7319. Ar << SavedByEngineVersion; // FEngineVersion
  7320. Ar << CompatibleWithEngineVersion; // FEngineVersion
  7321. Ar << InvalidReason; // EInvalidReason
  7322. // Debug info
  7323. Ar << AssetCount;
  7324. Ar << ThumbCount;
  7325. Ar << FileSize;
  7326. // Status
  7327. Ar << bParsed;
  7328. Ar << bValid;
  7329. Ar << bHasThumbnail;
  7330. Ar << bCompatible;
  7331. }
  7332. #endif
  7333. FORCEINLINE FArchive& operator<<(FArchive& Ar, FExtAssetDependencyInfo& DependencyInfo)
  7334. {
  7335. Ar << DependencyInfo.ValidDependentFiles;
  7336. Ar << DependencyInfo.InvalidDependentFiles;
  7337. Ar << DependencyInfo.MissingDependentFiles;
  7338. Ar << DependencyInfo.SkippedPackageNames;
  7339. Ar << DependencyInfo.MissingPackageNames;
  7340. Ar << DependencyInfo.AllDepdencyPackages;
  7341. Ar << DependencyInfo.AssetDepdencies;
  7342. Ar << DependencyInfo.AssetStatus;
  7343. return Ar;
  7344. }
  7345. FORCEINLINE FArchive& operator<<(FArchive& Ar, FExtAssetDependencyNode& DependencyNode)
  7346. {
  7347. Ar << DependencyNode.PackageName;
  7348. Ar << DependencyNode.ReferenceType;
  7349. Ar << DependencyNode.NodeStatus;
  7350. return Ar;
  7351. }
  7352. FORCEINLINE FArchive& operator<<(FArchive& Ar, FRootContentPathInfo& RootContentPathInfo)
  7353. {
  7354. Ar << RootContentPathInfo.ContentType;
  7355. Ar << RootContentPathInfo.AssetContentRoot;
  7356. Ar << RootContentPathInfo.DisplayName;
  7357. return Ar;
  7358. }
  7359. //////////////////////////////////////////////////////////////
  7360. #if ECB_WIP_CACHEDB
  7361. template<class Archive>
  7362. bool FExtAssetRegistryStateCache::SerializeForCache(Archive&& Ar)
  7363. {
  7364. return Ar.IsSaving() ? Save(Ar) : Load(Ar);
  7365. }
  7366. bool FExtAssetRegistryStateCache::Save(FArchive& OriginalAr)
  7367. {
  7368. check(!OriginalAr.IsLoading());
  7369. FExtAssetRegistryVersion::Type Version = FExtAssetRegistryVersion::LatestVersion;
  7370. FExtAssetRegistryVersion::SerializeVersion(OriginalAr, Version);
  7371. // Set up fixed asset registry writer
  7372. FExtAssetRegistryWriter Ar(OriginalAr);
  7373. // serialize number of objects
  7374. int32 AssetCount = CachedAssetsByFilePath.Num();
  7375. Ar << AssetCount;
  7376. NumAssetsInCache = AssetCount;
  7377. // Write asset data first
  7378. for (TPair<FName, FExtAssetData*>& Pair : CachedAssetsByFilePath)
  7379. {
  7380. FExtAssetData& AssetData(*Pair.Value);
  7381. AssetData.SerializeForCache(Ar);
  7382. }
  7383. SerializeDependencyInfos(Ar);
  7384. NumFoldersInCache = CachedSubPaths.Num();
  7385. SerializeMisc(Ar);
  7386. PrintStatus();
  7387. return !Ar.IsError();
  7388. }
  7389. bool FExtAssetRegistryStateCache::Load(FArchive& Ar)
  7390. {
  7391. check(Ar.IsLoading());
  7392. FExtAssetRegistryVersion::Type Version = FExtAssetRegistryVersion::LatestVersion;
  7393. FExtAssetRegistryVersion::SerializeVersion(Ar, Version);
  7394. FSoftObjectPathSerializationScope SerializationScope(NAME_None, NAME_None, ESoftObjectPathCollectType::NeverCollect, ESoftObjectPathSerializeType::AlwaysSerialize);
  7395. if (Version < FExtAssetRegistryVersion::SaveLoadNameBatch)
  7396. {
  7397. ECB_LOG(Warning, TEXT("Can't load cache db: version too old."));
  7398. return false;
  7399. }
  7400. else
  7401. {
  7402. FExtAssetRegistryReader Reader(Ar);
  7403. if (Reader.IsError())
  7404. {
  7405. return false;
  7406. }
  7407. Load(Reader, Version);
  7408. }
  7409. return !Ar.IsError();
  7410. }
  7411. template<class Archive>
  7412. void FExtAssetRegistryStateCache::Load(Archive&& Ar, FExtAssetRegistryVersion::Type Version)
  7413. {
  7414. // serialize number of objects
  7415. int32 LocalNumAssets = 0;
  7416. Ar << LocalNumAssets;
  7417. NumAssetsInCache = LocalNumAssets;
  7418. // allocate one single block for all asset data structs (to reduce tens of thousands of heap allocations)
  7419. TArrayView<FExtAssetData> PreallocatedAssetDataBuffer(new FExtAssetData[LocalNumAssets], LocalNumAssets);
  7420. PreallocatedAssetDataBuffers.Add(PreallocatedAssetDataBuffer.GetData());
  7421. for (FExtAssetData& NewAssetData : PreallocatedAssetDataBuffer)
  7422. {
  7423. NewAssetData.SerializeForCache(Ar);
  7424. }
  7425. SetAssetDatas(PreallocatedAssetDataBuffer);
  7426. SerializeDependencyInfos(Ar);
  7427. SerializeMisc(Ar);
  7428. NumFoldersInCache = CachedSubPaths.Num();
  7429. PrintStatus();
  7430. }
  7431. void FExtAssetRegistryStateCache::SetAssetDatas(TArrayView<FExtAssetData> AssetDatas)
  7432. {
  7433. int32 NumAssets = AssetDatas.Num();
  7434. // CachedAssetsByFolder
  7435. // CachedAssetsByFilePath
  7436. // CachedPackageNameToFilePathMap
  7437. // CachedUnParsedAssets
  7438. // CachedAssetsByClass
  7439. // CachedAssetsByTag
  7440. auto SetPathCache = [&]()
  7441. {
  7442. CachedAssetsByFilePath.Empty(AssetDatas.Num());
  7443. for (FExtAssetData& AssetData : AssetDatas)
  7444. {
  7445. CacheNewAsset(&AssetData);
  7446. }
  7447. };
  7448. SetPathCache();
  7449. }
  7450. void FExtAssetRegistryStateCache::SerializeDependencyInfos(FArchive& Ar)
  7451. {
  7452. Ar << CachedDependencyInfoByFilePath;
  7453. }
  7454. void FExtAssetRegistryStateCache::SerializeMisc(FArchive& Ar)
  7455. {
  7456. Ar << CachedRootContentPaths;
  7457. Ar << CachedSubPaths;
  7458. Ar << CachedEmptyFoldersStatus;
  7459. Ar << CachedFullyParsedFolders;
  7460. Ar << CachedFilePathsByFolder;
  7461. Ar << CachedAssetContentRoots;
  7462. Ar << CachedAssetContentType;
  7463. Ar << CachedAssetContentRootHosts;
  7464. Ar << CachedAssetContentRootConfigDirs;
  7465. Ar << CachedFolderColorIndices;
  7466. Ar << CachedFolderColors;
  7467. }
  7468. #endif
  7469. //////////////////////////////////////////////////////////////
  7470. #if ECB_WIP_CACHEDB
  7471. namespace CachDBFileUtil
  7472. {
  7473. bool SaveBufferToFile(const FString& FullFilePath, FBufferArchive& ToBinary)
  7474. {
  7475. //No Data
  7476. if (ToBinary.Num() <= 0) return false;
  7477. if (FFileHelper::SaveArrayToFile(ToBinary, *FullFilePath))
  7478. {
  7479. ToBinary.FlushCache();
  7480. ToBinary.Empty();
  7481. UE_LOG(LogECB, Warning, TEXT("[SaveBufferToFile] success!"));
  7482. return true;
  7483. }
  7484. // Free Binary Array
  7485. ToBinary.FlushCache();
  7486. ToBinary.Empty();
  7487. UE_LOG(LogECB, Warning, TEXT("[SaveBufferToFile] fail!"));
  7488. return false;
  7489. }
  7490. }
  7491. bool FExtAssetRegistry::LoadCacheDB(bool bSilent/* = true*/)
  7492. {
  7493. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  7494. FString FullFilePath = ExtContentBrowserSetting->CacheFilePath.FilePath;
  7495. TArray<uint8> TheBinaryArray;
  7496. if (!FFileHelper::LoadFileToArray(TheBinaryArray, *FullFilePath))
  7497. {
  7498. ECB_LOG(Warning, TEXT("[FExtAssetRegistry::LoadCacheDB] Failed to Load Cache DB from File!"));
  7499. return false;
  7500. }
  7501. //File Load Error
  7502. if (TheBinaryArray.Num() <= 0)
  7503. {
  7504. return false;
  7505. }
  7506. FMemoryReader* FromBinary = new FMemoryReader(TheBinaryArray, true);
  7507. //true, free data after done
  7508. FromBinary->Seek(0);
  7509. bool bSerializeResult = State.SerializeForCache(*FromBinary);
  7510. FromBinary->FlushCache();
  7511. // Empty & Close Buffer
  7512. TheBinaryArray.Empty();
  7513. FromBinary->Close();
  7514. delete FromBinary;
  7515. FromBinary = nullptr;
  7516. State.PrintStatus();
  7517. if (bSerializeResult && !bSilent)
  7518. {
  7519. FString Message = FString::Printf(TEXT("Cache file \"%s\" loaded."), *FullFilePath);
  7520. ExtContentBrowserUtils::NotifyMessage(Message);
  7521. }
  7522. return bSerializeResult;
  7523. }
  7524. bool FExtAssetRegistry::LoadCacheDBWithFilePicker()
  7525. {
  7526. return false;
  7527. }
  7528. bool FExtAssetRegistry::SaveCacheDB(bool bSilent/* = true*/)
  7529. {
  7530. UExtContentBrowserSettings* ExtContentBrowserSetting = GetMutableDefault<UExtContentBrowserSettings>();
  7531. FExtContentBrowserModule& ExtContentBrowserModule = FModuleManager::GetModuleChecked<FExtContentBrowserModule>(TEXT("ExtContentBrowser"));
  7532. FString FullFilePath = ExtContentBrowserSetting->CacheFilePath.FilePath;
  7533. FBufferArchive ToBinary;
  7534. if (State.SerializeForCache(ToBinary))
  7535. {
  7536. if (CachDBFileUtil::SaveBufferToFile(FullFilePath, ToBinary))
  7537. {
  7538. if (!bSilent)
  7539. {
  7540. FString Message = FString::Printf(TEXT("Cache file \"%s\" saved."), *FullFilePath);
  7541. ExtContentBrowserUtils::NotifyMessage(Message);
  7542. return true;
  7543. }
  7544. }
  7545. }
  7546. return false;
  7547. }
  7548. bool FExtAssetRegistry::PurgeCacheDB(bool bSilent/* = true*/)
  7549. {
  7550. if (State.PurgeAssets(bSilent))
  7551. {
  7552. return SaveCacheDB();
  7553. }
  7554. return false;
  7555. }
  7556. void FExtAssetRegistry::SwitchCacheMode()
  7557. {
  7558. StopAllBackgroundGathering();
  7559. State.Reset();
  7560. const UExtContentBrowserSettings* ExtContentBrowserSetting = GetDefault<UExtContentBrowserSettings>();
  7561. const bool bCacheMode = ExtContentBrowserSetting->bCacheMode;
  7562. if (bCacheMode)
  7563. {
  7564. LoadCacheDB();
  7565. }
  7566. {
  7567. LoadRootContentPaths();
  7568. }
  7569. }
  7570. #endif
  7571. #undef LOCTEXT_NAMESPACE