but I'm confused how some of the data is already present. For instance, we can already get the class name of the object, as is done in the loop.
Objects gets created before serialization. This is exactly how Unreal works itself. They're created as empty objects of particular class in UnPackage::CreateExport().
CreateExport is called, and somehow GetExport works. My confusion is that Exp.ObjectName already exists but not Exp.Object.
Exp.Name is a part of the package's export table. Exp.Object is a "transient" field, it doesn't exist in package itself. When package is loaded, it's NULL. Then, when CreateExport is called, it checks - if Exp.Object is not NULL, it's already created and returned. If not - the object is created according to its class name, with "factory" functions registered in global class system. The (empty) created object is placed into Exp.Object. It's similar to how Unreal works, it's hard to do things in a different way.
Then later in the function CreateClass is called. I assume that Type->Constructor(Obj) should be doing something, but following the debugger it doesn't seem like anything really happens here.
It calls object's constructor. Before it, the "object" is just a zeroed memory, even without having any virtual methods table. The constructor wrapper auto-generated in DECLARE_CLASS macro - it generates several "static" functions which calls class methods or returns class information, like it's name and parent class.
Later after BeginLoad is called, it seems that the real loading happens. Here when we check if OuterExp.Object exists, it does. I'm really not sure how it exists though. It doesn't seem like anything has happened yet that should've filled that object.
I think you meant EndLoad. The serialization is called there. It picks already created (but still empty, "defaulted") object, and calls its virtual Serialize method, passing FArchive as the object which represents a "stream of data" (could be a file, memory, everything). FArchive has many implementations, sometimes they're "nested". For instance, UnPackage is FArchive, but all of its serialization calls are passed to "Loader" member, which is ALSO FArchive. This allows to abstract from data storage methods - this could be a separate file in OS, could be a file in pak, could be a compressed upk file etc.
So I'm wondering where did the data for the object get parsed from that file? How did it happen? Am I completely off? Before I was checking the Serialize functions, where it seems data is literally read in from the file, but I don't know how it's getting parsed. A little overview would be helpful.
Data parsed in UObject::Serialize (function is overridden for most of classes, UObject::Serialize only parses UProperty data).