@@ -20,6 +20,7 @@ constexpr const char* kVersionDescriptorUrl =
2020 " https://piston-meta.mojang.com/v1/packages/715ccf3330885e75b205124f09f8712542cbe7e0/1.20.1.json" ;
2121
2222const HashSha1 kVersionDescriptorHash (" 715ccf3330885e75b205124f09f8712542cbe7e0" );
23+ const String kResourceApi = POLY_STR(" https://resources.download.minecraft.net/" );
2324
2425static json_object_s* FindJsonObjectElement (json_object_s* obj, const char * name) {
2526 json_object_element_s* element = obj->start ;
@@ -94,15 +95,20 @@ inline char* GetAbsolutePath(MemoryArena& arena, const String& path, const char*
9495 return buffer;
9596}
9697
97- inline bool GetOrCreateFolder (Platform& platform, const char * path) {
98- if (!platform.FolderExists (path)) {
99- if (!platform.CreateFolder (path)) {
100- fprintf (stderr, " Failed to create folder '%s'\n " , path);
101- return false ;
102- }
103- }
98+ inline char * GetObjectUrl (MemoryArena& arena, HashSha1 hash) {
99+ char hash_str[3 ] = {};
100+ u8 hash_value = hash.hash [0 ];
101+
102+ sprintf (hash_str, " %02x" , hash_value);
103+
104+ char full_hash[41 ];
105+ hash.ToString (full_hash);
106+
107+ char * buffer = (char *)arena.Allocate (kResourceApi .size + sizeof (full_hash) + sizeof (hash_str) + 1 );
104108
105- return true ;
109+ sprintf (buffer, " %.*s%s/%s" , (u32 )kResourceApi .size , kResourceApi .data , hash_str, full_hash);
110+
111+ return buffer;
106112}
107113
108114void AssetStore::Initialize () {
@@ -112,33 +118,23 @@ void AssetStore::Initialize() {
112118
113119 // Check local store for version descriptor file
114120 if (HasAsset (version_info)) {
115- ProcessVersionDescriptor (version_info);
121+ char * filename = GetAbsolutePath (trans_arena, path, " versions\\ %s" , kVersionDescriptor );
122+
123+ ProcessVersionDescriptor (filename);
116124 } else {
117125 net_queue.PushRequest (kVersionDescriptorUrl , this , [](NetworkRequest* request, NetworkResponse* response) {
118126 AssetStore* store = (AssetStore*)request->userp ;
119127
120128 char * filename = GetAbsolutePath (store->trans_arena , store->path , " versions\\ %s" , kVersionDescriptor );
121129
122130 response->SaveToFile (filename);
123- AssetInfo info;
124-
125- info.type = AssetType::VersionDescriptor;
126- info.exists = true ;
127- strcpy (info.path , filename);
128-
129- store->ProcessVersionDescriptor (info);
131+ store->ProcessVersionDescriptor (filename);
130132 });
131133 }
132134}
133135
134- void AssetStore::ProcessVersionDescriptor (const AssetInfo& info) {
135- //
136- // Load json and grab client/index
137-
138- // If not client, net_queue request
139- // if not index, net_queue request
140-
141- String contents = ReadEntireFile (info.path , trans_arena);
136+ void AssetStore::ProcessVersionDescriptor (const char * path) {
137+ String contents = ReadEntireFile (path, trans_arena);
142138
143139 json_value_s* root_value = json_parse (contents.data , contents.size );
144140 if (!root_value || root_value->type != json_type_object) {
@@ -215,86 +211,163 @@ void AssetStore::ProcessVersionDescriptor(const AssetInfo& info) {
215211 char * filename = GetAbsolutePath (store->trans_arena , store->path , " index\\ %s" , kVersionIndex );
216212
217213 response->SaveToFile (filename);
214+ store->ProcessIndex (filename);
215+ });
216+ } else {
217+ char * filename = GetAbsolutePath (trans_arena, this ->path , " index\\ %s" , kVersionIndex );
218218
219- AssetInfo index_info = {};
219+ ProcessIndex (filename);
220+ }
221+ }
222+ }
220223
221- index_info. type = AssetType::Index;
222- strcpy (index_info. path , filename );
224+ void AssetStore::ProcessIndex ( const char * filename) {
225+ String contents = ReadEntireFile (filename, trans_arena );
223226
224- store->ProcessIndex (index_info);
225- });
226- } else {
227- char * filename = GetAbsolutePath (trans_arena, path, " index\\ %s" , kVersionIndex );
227+ json_value_s* root_value = json_parse (contents.data , contents.size );
228+ if (!root_value || root_value->type != json_type_object) {
229+ fprintf (stderr, " AssetStore: Failed to parse version index json.\n " );
230+ exit (1 );
231+ }
232+
233+ json_object_s* root = json_value_as_object (root_value);
234+ json_object_s* objects = FindJsonObjectElement (root, " objects" );
235+
236+ if (!objects) {
237+ fprintf (stderr, " AssetStore: Invalid 'objects' element of version index. Expected object.\n " );
238+ exit (1 );
239+ }
240+
241+ json_object_element_s* object_element = objects->start ;
242+ while (object_element) {
243+ String element_name (object_element->name ->string , object_element->name ->string_size );
244+
245+ json_object_element_s* current_obj_ele = object_element;
246+
247+ object_element = object_element->next ;
248+
249+ // Skip over objects that aren't currently necessary.
250+ if (poly_contains (element_name, POLY_STR (" sound" ))) continue ;
251+ if (poly_contains (element_name, POLY_STR (" /lang/" ))) continue ;
252+ if (poly_contains (element_name, POLY_STR (" icons/" ))) continue ;
253+ if (poly_contains (element_name, POLY_STR (" /resourcepacks/" ))) continue ;
254+
255+ if (current_obj_ele->value ->type == json_type_object) {
256+ json_object_s* obj = json_value_as_object (current_obj_ele->value );
257+
258+ String obj_hash_str = FindJsonStringValue (obj, " hash" );
259+
260+ if (obj_hash_str.size > 0 ) {
261+ HashSha1 hash (obj_hash_str.data , obj_hash_str.size );
262+
263+ AssetInfo info = {};
264+ info.type = AssetType::Object;
265+ info.hash = hash;
266+
267+ asset_hash_map.Insert (element_name, hash);
268+
269+ // Check local store for item.
270+ // If not found, request from server.
271+ if (!HasAsset (info)) {
272+ char * url = GetObjectUrl (trans_arena, hash);
228273
229- strcpy (index_info.path , filename);
274+ net_queue.PushRequest (url, this , [](NetworkRequest* request, NetworkResponse* response) {
275+ AssetStore* store = (AssetStore*)request->userp ;
230276
231- ProcessIndex (index_info);
277+ char * relative_name = request->url + kResourceApi .size ;
278+ char * filename = GetAbsolutePath (store->trans_arena , store->path , " objects\\ %s" , relative_name);
279+
280+ response->SaveToFile (filename);
281+ });
282+ }
283+ }
232284 }
233285 }
234286}
235287
236- void AssetStore::ProcessIndex (const AssetInfo& info) {
237- printf (" TODO: Process index. (%s)\n " , info.path );
288+ String AssetStore::LoadObject (MemoryArena& arena, String name) {
289+ HashSha1* hash = asset_hash_map.Find (name);
290+
291+ if (hash) {
292+ char hash_str[3 ] = {};
293+ u8 hash_value = hash->hash [0 ];
294+
295+ sprintf (hash_str, " %02x" , hash_value);
296+
297+ char * objects_folder = GetAbsolutePath (trans_arena, path, " objects" );
298+ if (platform.FolderExists (objects_folder)) {
299+ char * hash_folder = GetAbsolutePath (trans_arena, path, " objects\\ %s" , hash_str);
300+
301+ if (platform.FolderExists (hash_folder)) {
302+ char fullhash[41 ];
303+
304+ hash->ToString (fullhash);
305+
306+ char * filename = GetAbsolutePath (trans_arena, path, " objects\\ %s\\ %s" , hash_str, fullhash);
307+
308+ return ReadEntireFile (filename, arena);
309+ }
310+ }
311+ }
312+
313+ fprintf (stderr, " AssetStore::GetAsset failed to find asset with name %.*s\n " , (u32 )name.size , name.data );
314+ return {};
238315}
239316
240317bool AssetStore::HasAsset (AssetInfo& info) {
318+ char * filename = nullptr ;
319+
320+ ArenaSnapshot snapshot = trans_arena.GetSnapshot ();
321+
241322 switch (info.type ) {
242323 case AssetType::Client: {
243324 char * versions_folder = GetAbsolutePath (trans_arena, path, " versions" );
244325 if (!platform.FolderExists (versions_folder)) return false ;
245326
246- char * filename = GetAbsolutePath (trans_arena, path, " versions\\ %s" , kVersionJar );
247- HashSha1 existing_hash = GetFileSha1 (trans_arena, filename);
248-
249- return existing_hash == info.hash ;
327+ filename = GetAbsolutePath (trans_arena, path, " versions\\ %s" , kVersionJar );
250328 } break ;
251329 case AssetType::VersionDescriptor: {
252330 char * index_folder = GetAbsolutePath (trans_arena, path, " versions" );
253331 if (!platform.FolderExists (index_folder)) return false ;
254332
255- char * filename = GetAbsolutePath (trans_arena, path, " versions\\ %s" , kVersionDescriptor );
256- HashSha1 existing_hash = GetFileSha1 (trans_arena, filename);
257-
258- if (existing_hash != info.hash ) {
259- return false ;
260- }
261-
262- strcpy (info.path , filename);
263- return true ;
333+ filename = GetAbsolutePath (trans_arena, path, " versions\\ %s" , kVersionDescriptor );
264334 } break ;
265335 case AssetType::Index: {
266336 char * index_folder = GetAbsolutePath (trans_arena, path, " index" );
267337 if (!platform.FolderExists (index_folder)) return false ;
268338
269- char * filename = GetAbsolutePath (trans_arena, path, " index\\ %s" , kVersionIndex );
270- HashSha1 existing_hash = GetFileSha1 (trans_arena, filename);
271-
272- return existing_hash == info.hash ;
339+ filename = GetAbsolutePath (trans_arena, path, " index\\ %s" , kVersionIndex );
273340 } break ;
274341 case AssetType::Object: {
275- char hash_str [3 ] = {};
342+ char minihash [3 ] = {};
276343 u8 hash_value = info.hash .hash [0 ];
277344
278- sprintf (hash_str, " %02x" , hash_value);
345+ sprintf (minihash, " %02x" , hash_value);
346+ char fullhash[41 ];
347+ info.hash .ToString (fullhash);
279348
280349 char * objects_folder = GetAbsolutePath (trans_arena, path, " objects" );
281350 if (!platform.FolderExists (objects_folder)) return false ;
282351
283- char * hash_folder = GetAbsolutePath (trans_arena, path, " objects\\ %s" , hash_str );
352+ char * hash_folder = GetAbsolutePath (trans_arena, path, " objects\\ %s" , minihash );
284353 if (!platform.FolderExists (hash_folder)) return false ;
285354
286- char * filename =
287- GetAbsolutePath (trans_arena, path, " objects\\ %s\\ %.*s" , hash_str, (u32 )info.name .size , info.name .data );
288-
289- HashSha1 existing_hash = GetFileSha1 (trans_arena, filename);
290-
291- return existing_hash == info.hash ;
355+ filename = GetAbsolutePath (trans_arena, path, " objects\\ %s\\ %s" , minihash, fullhash);
292356 } break ;
293357 default : {
294358 } break ;
295359 }
296360
297- return false ;
361+ bool exists = false ;
362+
363+ if (filename) {
364+ HashSha1 existing_hash = GetFileSha1 (trans_arena, filename);
365+ exists = existing_hash == info.hash ;
366+ }
367+
368+ trans_arena.Revert (snapshot);
369+
370+ return exists;
298371}
299372
300373char * AssetStore::GetClientPath (MemoryArena& arena) {
0 commit comments