bool UTexture::GetTextureData(CTextureData &TexData) const { guard(UTexture::GetTextureData); TexData.Platform = PLATFORM_PC; TexData.OriginalFormatEnum = Format; TexData.OriginalFormatName = EnumToName("ETextureFormat", Format); TexData.Obj = this; TexData.Palette = Palette; const FArchive* PackageAr = GetPackageArchive(); // process external sources for some games #if BIOSHOCK if (PackageAr && PackageAr->Game == GAME_Bioshock && CachedBulkDataSize) //?? check bStripped or Baked ? { byte* CompressedData = FindBioTexture(this); // may be NULL if (CompressedData) { CMipMap* DstMip = new (TexData.Mips) CMipMap; DstMip->CompressedData = CompressedData; DstMip->ShouldFreeData = true; DstMip->USize = USize; DstMip->VSize = VSize; DstMip->DataSize = (int)CachedBulkDataSize; } TexData.Platform = PackageAr->Platform; } #endif // BIOSHOCK #if UC2 if (PackageAr && PackageAr->Engine() == GAME_UE2X) { // try to find texture inside XBox xpr files int DataSize; byte* CompressedData = FindXprData(Name, &DataSize); // may be NULL if (CompressedData) { CMipMap* DstMip = new (TexData.Mips) CMipMap; DstMip->CompressedData = CompressedData; DstMip->ShouldFreeData = true; DstMip->USize = USize; DstMip->VSize = VSize; DstMip->DataSize = DataSize; } } #endif // UC2 if (TexData.Mips.Num() == 0) { // texture was not taken from external source for (int n = 0; n < Mips.Num(); n++) { // find 1st mipmap with non-null data array // reference: DemoPlayerSkins.utx/DemoSkeleton have null-sized 1st 2 mips const FMipmap &Mip = Mips[n]; if (!Mip.DataArray.Num()) continue; CMipMap* DstMip = new (TexData.Mips) CMipMap; DstMip->CompressedData = &Mip.DataArray[0]; DstMip->ShouldFreeData = false; DstMip->USize = Mip.USize; DstMip->VSize = Mip.VSize; DstMip->DataSize = Mip.DataArray.Num(); break; } } ETexturePixelFormat intFormat; //?? return old code back - UE1 and UE2 differs in codes 6 and 7 only if (Package && (PackageAr->Engine() == GAME_UE1)) { // UE1 has different ETextureFormat layout switch (Format) { case 0: intFormat = TPF_P8; break; // case 1: // intFormat = TPF_RGB32; // in script source code: TEXF_RGB32, but TEXF_RGBA7 in .h // break; // case 2: // intFormat = TPF_RGB64; // in script source code: TEXF_RGB64, but TEXF_RGB16 in .h // break; case 3: intFormat = TPF_DXT1; break; case 4: intFormat = TPF_RGB8; break; case 5: intFormat = TPF_BGRA8; break; // newer UE1 versions has DXT3 and DXT5 case 6: intFormat = TPF_DXT3; break; case 7: intFormat = TPF_DXT5; break; default: appNotify("Unknown UE1 texture format: %d", Format); return false; } } else { // UE2 switch (Format) { case TEXF_P8: intFormat = TPF_P8; break; case TEXF_DXT1: intFormat = TPF_DXT1; break; case TEXF_RGB8: intFormat = TPF_RGB8; break; case TEXF_RGBA8: intFormat = TPF_BGRA8; break; case TEXF_DXT3: intFormat = TPF_DXT3; break; case TEXF_DXT5: intFormat = TPF_DXT5; break; case TEXF_L8: intFormat = TPF_G8; break; case TEXF_CxV8U8: intFormat = TPF_V8U8_2; break; case TEXF_DXT5N: intFormat = TPF_DXT5N; break; case TEXF_3DC: intFormat = TPF_BC5; break; default: appNotify("Unknown UE2 texture format: %s (%d)", TexData.OriginalFormatName, Format); return false; } } TexData.Format = intFormat; #if BIOSHOCK && SUPPORT_XBOX360 if (TexData.Mips.Num() && TexData.Platform == PLATFORM_XBOX360) { for (int MipLevel = 0; MipLevel < TexData.Mips.Num(); MipLevel++) TexData.DecodeXBox360(MipLevel); } #endif #if BIOSHOCK if (Package && PackageAr->Game == GAME_Bioshock) { // This game has DataSize stored for all mipmaps, we should compute side of 1st mipmap // in order to accept this value when uploading texture to video card (some vendors rejects // large values) //?? Place code to CTextureData method? const CPixelFormatInfo &Info = PixelFormatInfo[intFormat]; int numBlocks = TexData.Mips[0].USize * TexData.Mips[0].VSize / (Info.BlockSizeX * Info.BlockSizeY); // used for validation only int requiredDataSize = numBlocks * Info.BytesPerBlock; if (requiredDataSize > TexData.Mips[0].DataSize) { appNotify("Bioshock texture %s: data too small; %dx%d, requires %X bytes, got %X\n", Name, TexData.Mips[0].USize, TexData.Mips[0].VSize, requiredDataSize, TexData.Mips[0].DataSize); } else if (requiredDataSize < TexData.Mips[0].DataSize) { // appPrintf("Bioshock texture %s: stripping data size from %X to %X\n", Name, TexData.Mips[0].DataSize, requiredDataSize); TexData.Mips[0].DataSize = requiredDataSize; } } #endif // BIOSHOCK return (TexData.Mips.Num() > 0); unguardf("%s", Name); }
void USkeleton::ConvertAnims(UAnimSequence4* Seq) { guard(USkeleton::ConvertAnims); CAnimSet* AnimSet = ConvertedAnim; if (!AnimSet) { AnimSet = new CAnimSet(this); ConvertedAnim = AnimSet; // Copy bone names AnimSet->TrackBoneNames.Empty(ReferenceSkeleton.RefBoneInfo.Num()); for (int i = 0; i < ReferenceSkeleton.RefBoneInfo.Num(); i++) { AnimSet->TrackBoneNames.Add(ReferenceSkeleton.RefBoneInfo[i].Name); } //TODO: verify if UE4 has AnimRotationOnly stuff AnimSet->AnimRotationOnly = false; } if (!Seq) return; // allow calling ConvertAnims(NULL) to create empty AnimSet // DBG("----------- Skeleton %s: %d seq, %d bones -----------\n", Name, Anims.Num(), ReferenceSkeleton.RefBoneInfo.Num()); int NumTracks = Seq->GetNumTracks(); #if DEBUG_DECOMPRESS appPrintf("Sequence %s: %d bones, %d offsets (%g per bone), %d frames, %d compressed data\n" " trans %s, rot %s, scale %s, key %s\n", Seq->Name, NumTracks, Seq->CompressedTrackOffsets.Num(), Seq->CompressedTrackOffsets.Num() / (float)NumTracks, Seq->NumFrames, Seq->CompressedByteStream.Num(), EnumToName(Seq->TranslationCompressionFormat), EnumToName(Seq->RotationCompressionFormat), EnumToName(Seq->ScaleCompressionFormat), EnumToName(Seq->KeyEncodingFormat) ); for (int i2 = 0; i2 < Seq->CompressedTrackOffsets.Num(); /*empty*/) { if (Seq->KeyEncodingFormat != AKF_PerTrackCompression) { FName BoneName = ReferenceSkeleton.RefBoneInfo[Seq->GetTrackBoneIndex(i2/4)].Name; int TransOffset = Seq->CompressedTrackOffsets[i2 ]; int TransKeys = Seq->CompressedTrackOffsets[i2+1]; int RotOffset = Seq->CompressedTrackOffsets[i2+2]; int RotKeys = Seq->CompressedTrackOffsets[i2+3]; appPrintf(" [%d] = trans %d[%d] rot %d[%d] - %s\n", i2/4, TransOffset, TransKeys, RotOffset, RotKeys, *BoneName); i2 += 4; } else { FName BoneName = ReferenceSkeleton.RefBoneInfo[Seq->GetTrackBoneIndex(i2/2)].Name; int TransOffset = Seq->CompressedTrackOffsets[i2 ]; int RotOffset = Seq->CompressedTrackOffsets[i2+1]; appPrintf(" [%d] = trans %d rot %d - %s\n", i2/2, TransOffset, RotOffset, *BoneName); i2 += 2; } } #endif // DEBUG_DECOMPRESS // some checks int offsetsPerBone = 4; if (Seq->KeyEncodingFormat == AKF_PerTrackCompression) offsetsPerBone = 2; if (Seq->CompressedTrackOffsets.Num() != NumTracks * offsetsPerBone && !Seq->RawAnimationData.Num()) { appNotify("AnimSequence %s has wrong CompressedTrackOffsets size (has %d, expected %d), removing track", Seq->Name, Seq->CompressedTrackOffsets.Num(), NumTracks * offsetsPerBone); return; } // create CAnimSequence CAnimSequence *Dst = new CAnimSequence; AnimSet->Sequences.Add(Dst); Dst->Name = Seq->Name; Dst->NumFrames = Seq->NumFrames; Dst->Rate = Seq->NumFrames / Seq->SequenceLength * Seq->RateScale; // bone tracks ... Dst->Tracks.Empty(NumTracks); FMemReader Reader(Seq->CompressedByteStream.GetData(), Seq->CompressedByteStream.Num()); Reader.SetupFrom(*Package); bool HasTimeTracks = (Seq->KeyEncodingFormat == AKF_VariableKeyLerp); for (int BoneIndex = 0; BoneIndex < ReferenceSkeleton.RefBoneInfo.Num(); BoneIndex++) { CAnimTrack *A = new (Dst->Tracks) CAnimTrack; int TrackIndex = Seq->FindTrackForBoneIndex(BoneIndex); if (TrackIndex < 0) { // this track has no animation, use static pose from ReferenceSkeleton const FTransform& RefPose = ReferenceSkeleton.RefBonePose[BoneIndex]; A->KeyPos.Add(CVT(RefPose.Translation)); A->KeyQuat.Add(CVT(RefPose.Rotation)); //!! RefPose.Scale3D continue; } int k; if (!Seq->CompressedTrackOffsets.Num()) //?? or if RawAnimData.Num() != 0 { // using RawAnimData array assert(Seq->RawAnimationData.Num() == NumTracks); CopyArray(A->KeyPos, CVT(Seq->RawAnimationData[TrackIndex].PosKeys)); CopyArray(A->KeyQuat, CVT(Seq->RawAnimationData[TrackIndex].RotKeys)); CopyArray(A->KeyTime, Seq->RawAnimationData[TrackIndex].KeyTimes); // may be empty for (int k = 0; k < A->KeyTime.Num(); k++) A->KeyTime[k] *= Dst->Rate; continue; } FVector Mins, Ranges; // common ... static const CVec3 nullVec = { 0, 0, 0 }; static const CQuat nullQuat = { 0, 0, 0, 1 }; int offsetIndex = TrackIndex * offsetsPerBone; // PARAGON has invalid data inside some animation tracks. Not sure if game engine ignores them // or trying to process (this game has holes in data due to wrong pointers in CompressedTrackOffsets). // This causes garbage data to appear instead of real animation track header, with wrong compression // method etc. We're going to skip such tracks with displaying a warning message. if (0) // this is just a placeholder for error handler - it should be located somewhere { track_error: AnimSet->Sequences.RemoveSingle(Dst); delete Dst; return; } //---------------------------------------------- // decode AKF_PerTrackCompression data //---------------------------------------------- if (Seq->KeyEncodingFormat == AKF_PerTrackCompression) { // this format uses different key storage guard(PerTrackCompression); assert(Seq->TranslationCompressionFormat == ACF_Identity); assert(Seq->RotationCompressionFormat == ACF_Identity); int TransOffset = Seq->CompressedTrackOffsets[offsetIndex ]; int RotOffset = Seq->CompressedTrackOffsets[offsetIndex+1]; uint32 PackedInfo; AnimationCompressionFormat KeyFormat; int ComponentMask; int NumKeys; #define DECODE_PER_TRACK_INFO(info) \ KeyFormat = (AnimationCompressionFormat)(info >> 28); \ ComponentMask = (info >> 24) & 0xF; \ NumKeys = info & 0xFFFFFF; \ HasTimeTracks = (ComponentMask & 8) != 0; guard(TransKeys); // read translation keys if (TransOffset == -1) { A->KeyPos.Add(nullVec); DBG(" [%d] no translation data\n", TrackIndex); } else { Reader.Seek(TransOffset); Reader << PackedInfo; DECODE_PER_TRACK_INFO(PackedInfo); A->KeyPos.Empty(NumKeys); DBG(" [%d] trans: fmt=%d (%s), %d keys, mask %d\n", TrackIndex, KeyFormat, EnumToName(KeyFormat), NumKeys, ComponentMask ); if (KeyFormat == ACF_IntervalFixed32NoW) { // read mins/maxs Mins.Set(0, 0, 0); Ranges.Set(0, 0, 0); if (ComponentMask & 1) Reader << Mins.X << Ranges.X; if (ComponentMask & 2) Reader << Mins.Y << Ranges.Y; if (ComponentMask & 4) Reader << Mins.Z << Ranges.Z; } for (k = 0; k < NumKeys; k++) { switch (KeyFormat) { // case ACF_None: case ACF_Float96NoW: { FVector v; if (ComponentMask & 7) { v.Set(0, 0, 0); if (ComponentMask & 1) Reader << v.X; if (ComponentMask & 2) Reader << v.Y; if (ComponentMask & 4) Reader << v.Z; } else { // ACF_Float96NoW has a special case for ((ComponentMask & 7) == 0) Reader << v; } A->KeyPos.Add(CVT(v)); } break; TPR(ACF_IntervalFixed32NoW, FVectorIntervalFixed32) case ACF_Fixed48NoW: { uint16 X, Y, Z; CVec3 v; v.Set(0, 0, 0); if (ComponentMask & 1) { Reader << X; v[0] = DecodeFixed48_PerTrackComponent<7>(X); } if (ComponentMask & 2) { Reader << Y; v[1] = DecodeFixed48_PerTrackComponent<7>(Y); } if (ComponentMask & 4) { Reader << Z; v[2] = DecodeFixed48_PerTrackComponent<7>(Z); } A->KeyPos.Add(v); } break; case ACF_Identity: A->KeyPos.Add(nullVec); break; default: { char buf[1024]; Seq->GetFullName(buf, 1024); appNotify("%s: unknown translation compression method: %d (%s) - dropping track", buf, KeyFormat, EnumToName(KeyFormat)); goto track_error; } } } // align to 4 bytes Reader.Seek(Align(Reader.Tell(), 4)); if (HasTimeTracks) ReadTimeArray(Reader, NumKeys, A->KeyPosTime, Seq->NumFrames); } unguard; guard(RotKeys); // read rotation keys if (RotOffset == -1) { A->KeyQuat.Add(nullQuat); DBG(" [%d] no rotation data\n", TrackIndex); } else { Reader.Seek(RotOffset); Reader << PackedInfo; DECODE_PER_TRACK_INFO(PackedInfo); A->KeyQuat.Empty(NumKeys); DBG(" [%d] rot : fmt=%d (%s), %d keys, mask %d\n", TrackIndex, KeyFormat, EnumToName(KeyFormat), NumKeys, ComponentMask ); if (KeyFormat == ACF_IntervalFixed32NoW) { // read mins/maxs Mins.Set(0, 0, 0); Ranges.Set(0, 0, 0); if (ComponentMask & 1) Reader << Mins.X << Ranges.X; if (ComponentMask & 2) Reader << Mins.Y << Ranges.Y; if (ComponentMask & 4) Reader << Mins.Z << Ranges.Z; } for (k = 0; k < NumKeys; k++) { switch (KeyFormat) { // TR (ACF_None, FQuat) case ACF_Float96NoW: { FQuatFloat96NoW q; Reader << q; FQuat q2 = q; // convert A->KeyQuat.Add(CVT(q2)); } break; case ACF_Fixed48NoW: { FQuatFixed48NoW q; q.X = q.Y = q.Z = 32767; // corresponds to 0 if (ComponentMask & 1) Reader << q.X; if (ComponentMask & 2) Reader << q.Y; if (ComponentMask & 4) Reader << q.Z; FQuat q2 = q; // convert A->KeyQuat.Add(CVT(q2)); } break; TR (ACF_Fixed32NoW, FQuatFixed32NoW) TRR(ACF_IntervalFixed32NoW, FQuatIntervalFixed32NoW) TR (ACF_Float32NoW, FQuatFloat32NoW) case ACF_Identity: A->KeyQuat.Add(nullQuat); break; default: { char buf[1024]; Seq->GetFullName(buf, 1024); appNotify("%s: unknown rotation compression method: %d (%s) - dropping track", buf, KeyFormat, EnumToName(KeyFormat)); goto track_error; } } } // align to 4 bytes Reader.Seek(Align(Reader.Tell(), 4)); if (HasTimeTracks) ReadTimeArray(Reader, NumKeys, A->KeyQuatTime, Seq->NumFrames); } unguard; unguard; continue; // end of AKF_PerTrackCompression block ... } //---------------------------------------------- // end of AKF_PerTrackCompression decoder //---------------------------------------------- // read animations int TransOffset = Seq->CompressedTrackOffsets[offsetIndex ]; int TransKeys = Seq->CompressedTrackOffsets[offsetIndex+1]; int RotOffset = Seq->CompressedTrackOffsets[offsetIndex+2]; int RotKeys = Seq->CompressedTrackOffsets[offsetIndex+3]; // appPrintf("[%d:%d:%d] : %d[%d] %d[%d] %d[%d]\n", j, Seq->RotationCompressionFormat, Seq->TranslationCompressionFormat, TransOffset, TransKeys, RotOffset, RotKeys, ScaleOffset, ScaleKeys); A->KeyPos.Empty(TransKeys); A->KeyQuat.Empty(RotKeys); // read translation keys if (TransKeys) { Reader.Seek(TransOffset); AnimationCompressionFormat TranslationCompressionFormat = Seq->TranslationCompressionFormat; if (TransKeys == 1) TranslationCompressionFormat = ACF_None; // single key is stored without compression // read mins/ranges if (TranslationCompressionFormat == ACF_IntervalFixed32NoW) { Reader << Mins << Ranges; } for (k = 0; k < TransKeys; k++) { switch (TranslationCompressionFormat) { TP (ACF_None, FVector) TP (ACF_Float96NoW, FVector) TPR(ACF_IntervalFixed32NoW, FVectorIntervalFixed32) TP (ACF_Fixed48NoW, FVectorFixed48) case ACF_Identity: A->KeyPos.Add(nullVec); break; default: appError("Unknown translation compression method: %d (%s)", TranslationCompressionFormat, EnumToName(TranslationCompressionFormat)); } } // align to 4 bytes Reader.Seek(Align(Reader.Tell(), 4)); if (HasTimeTracks) ReadTimeArray(Reader, TransKeys, A->KeyPosTime, Seq->NumFrames); } else { // A->KeyPos.Add(nullVec); // appNotify("No translation keys!"); }