// SPDX-License-Identifier: Unlicense

#include "sng.h"

#ifdef SHR3D_PSARC

#include "rijndael.h"
#include "inflate.h"
#include "helper.h"

#include <string.h>

static const u8 sngKey[32] = {
  0xCB, 0x64, 0x8D, 0xF3, 0xD1, 0x2A, 0x16, 0xBF,
  0x71, 0x70, 0x14, 0x14, 0xE6, 0x96, 0x19, 0xEC,
  0x17, 0x1C, 0xCA, 0x5D, 0x2A, 0x14, 0x2E, 0x3E,
  0x59, 0xDE, 0x7A, 0xDD, 0xA1, 0x8A, 0x3A, 0x30
};

static std::vector<u8> decryptSngData(const std::vector<u8>& sngData)
{
  const u32 magicNumber = u32_le(&sngData[0]);

  ASSERT(magicNumber == 0x4A);

  u8 iv[16];
  memcpy(iv, &sngData[8], sizeof(iv));

  const i64 len = sngData.size() - 24;

  const i64 paddedLen = ((len + 15) / 16) * 16; // round size up to the next 16 bytes so that the decrypt does not write outside the bounds.
  std::vector<u8> decrypedData(paddedLen);
  for (i64 i = 0; i < len; i += 16)
  {
    Rijndael::decrypt(sngKey, &sngData[24 + i], &decrypedData[i], 16, iv);
    {
      bool carry = true;
      for (i64 j = (sizeof(iv)) - 1; j >= 0 && carry; j--)
        carry = ((iv[j] = (u8)(iv[j] + 1)) == 0);
    }
  }

  //decrypedData.resize(len); // Not really neccesary, but get rid of the 0s from the padding at the end

  return decrypedData;
}

static std::vector<u8> inflateSngPlainText(const std::vector<u8>& decrypedSngData)
{
  const i32 plainTextLen = u32_le(&decrypedSngData[0]);
  const u16 zHeader = u16_le(&decrypedSngData[4]);
  ASSERT(zHeader == 0x78DA || zHeader == 0xDA78); //LE 55928 //BE 30938

  std::vector<u8> plainText(plainTextLen);
  Inflate::inflate(&decrypedSngData[4], i32(decrypedSngData.size() - 4), &plainText[0], plainTextLen);

  return plainText;
}

Sng::Info Sng::parse(const std::vector<u8>& sngData)
{
  Sng::Info sngInfo;

  const std::vector<u8> decrypedSngData = decryptSngData(sngData);
  const std::vector<u8> plainText = inflateSngPlainText(decrypedSngData);

  u64 j = 0;
  {
    const i32 bpmCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.bpm.resize(bpmCount);
    for (i32 i = 0; i < bpmCount; ++i)
    {
      sngInfo.bpm[i].time = f32_le(&plainText[j]);
      j += 4;
      sngInfo.bpm[i].measure = i16_le(&plainText[j]);
      j += 2;
      sngInfo.bpm[i].beat = i16_le(&plainText[j]);
      j += 2;
      sngInfo.bpm[i].phraseIteration = i32_le(&plainText[j]);
      j += 4;
      sngInfo.bpm[i].mask = i32_le(&plainText[j]);
      j += 4;
    }
  }

  {
    const i32 phraseCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.phrase.resize(phraseCount);
    for (i32 i = 0; i < phraseCount; ++i)
    {
      sngInfo.phrase[i].solo = plainText[j];
      j += 1;
      sngInfo.phrase[i].disparity = plainText[j];
      j += 1;
      sngInfo.phrase[i].ignore = plainText[j];
      j += 1;
      sngInfo.phrase[i].paddin = plainText[j];
      j += 1;
      sngInfo.phrase[i].maxDifficulty = i32_le(&plainText[j]);
      j += 4;
      sngInfo.phrase[i].phraseIterationLinks = i32_le(&plainText[j]);
      j += 4;
      memcpy(&sngInfo.phrase[i].name, &plainText[j], 32);
      j += 32;
    }
  }

  {
    const i32 chordCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.chord.resize(chordCount);
    for (i32 i = 0; i < chordCount; ++i)
    {
      sngInfo.chord[i].mask = ChordMask(plainText[j]);
      j += 4;
      memcpy(&sngInfo.chord[i].frets, &plainText[j], 6);
      j += 6;
      memcpy(&sngInfo.chord[i].fingers, &plainText[j], 6);
      j += 6;
      memcpy(&sngInfo.chord[i].notes, &plainText[j], 24);
      j += 24;
      memcpy(&sngInfo.chord[i].name, &plainText[j], 32);
      j += 32;
    }
  }

  {
    const i32 chordNotesCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.chordNotes.resize(chordNotesCount);
    for (i32 i = 0; i < chordNotesCount; ++i)
    {
      memcpy(&sngInfo.chordNotes[i].noteMask, &plainText[j], 24);
      j += 24;
      memcpy(&sngInfo.chordNotes[i].bendData, &plainText[j], 2328);
      j += 2328;
      memcpy(&sngInfo.chordNotes[i].slideTo, &plainText[j], 6);
      j += 6;
      memcpy(&sngInfo.chordNotes[i].slideUnpitchTo, &plainText[j], 6);
      j += 6;
      memcpy(&sngInfo.chordNotes[i].vibrato, &plainText[j], 12);
      j += 12;
    }
  }

  {
    const i32 vocalCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.vocal.resize(vocalCount);
    for (i32 i = 0; i < vocalCount; ++i)
    {
      sngInfo.vocal[i].time = f32_le(&plainText[j]);
      j += 4;
      sngInfo.vocal[i].note = i32_le(&plainText[j]);
      j += 4;
      sngInfo.vocal[i].length = f32_le(&plainText[j]);
      j += 4;
      memcpy(&sngInfo.vocal[i].lyric, &plainText[j], 48);
      j += 48;
    }
  }

  if (sngInfo.vocal.size() > 0)
  {
    {
      const i32 symbolsHeaderCount = u32_le(&plainText[j]);
      j += 4;
      sngInfo.symbolsHeader.resize(symbolsHeaderCount);
      for (i32 i = 0; i < symbolsHeaderCount; ++i)
      {
        sngInfo.symbolsHeader[i].unk1 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsHeader[i].unk2 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsHeader[i].unk3 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsHeader[i].unk4 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsHeader[i].unk5 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsHeader[i].unk6 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsHeader[i].unk7 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsHeader[i].unk8 = i32_le(&plainText[j]);
        j += 4;
      }
    }

    {
      const i32 symbolsTextureCount = u32_le(&plainText[j]);
      j += 4;
      sngInfo.symbolsTexture.resize(symbolsTextureCount);
      for (i32 i = 0; i < symbolsTextureCount; ++i)
      {
        memcpy(&sngInfo.symbolsTexture[i].font, &plainText[j], 128);
        j += 128;
        sngInfo.symbolsTexture[i].fontpathLength = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsTexture[i].unk10 = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsTexture[i].width = i32_le(&plainText[j]);
        j += 4;
        sngInfo.symbolsTexture[i].height = i32_le(&plainText[j]);
        j += 4;
      }
    }

    {
      const i32 symbolDefinitionCount = u32_le(&plainText[j]);
      j += 4;
      sngInfo.symbolDefinition.resize(symbolDefinitionCount);
      for (i32 i = 0; i < symbolDefinitionCount; ++i)
      {
        memcpy(&sngInfo.symbolDefinition[i].text, &plainText[j], 12);
        j += 12;
        memcpy(&sngInfo.symbolDefinition[i].rectOutter, &plainText[j], 16);
        j += 16;
        memcpy(&sngInfo.symbolDefinition[i].rectInner, &plainText[j], 16);
        j += 16;
      }
    }
  }

  {
    const i32 phraseIterationCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.phraseIteration.resize(phraseIterationCount);
    for (i32 i = 0; i < phraseIterationCount; ++i)
    {
      sngInfo.phraseIteration[i].phraseId = i32_le(&plainText[j]);
      j += 4;
      sngInfo.phraseIteration[i].startTime = f32_le(&plainText[j]);
      j += 4;
      sngInfo.phraseIteration[i].nextPhraseTime = f32_le(&plainText[j]);
      j += 4;
      memcpy(&sngInfo.phraseIteration[i].difficulty, &plainText[j], 12);
      j += 12;
    }
  }

  {
    const i32 phraseExtraInfoByLevelCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.phraseExtraInfoByLevel.resize(phraseExtraInfoByLevelCount);
    for (i32 i = 0; i < phraseExtraInfoByLevelCount; ++i)
    {
      sngInfo.phraseExtraInfoByLevel[i].phraseId = i32_le(&plainText[j]);
      j += 4;
      sngInfo.phraseExtraInfoByLevel[i].difficulty = i32_le(&plainText[j]);
      j += 4;
      sngInfo.phraseExtraInfoByLevel[i].empty = i32_le(&plainText[j]);
      j += 4;
      sngInfo.phraseExtraInfoByLevel[i].levelJump = plainText[j];
      j += 1;
      sngInfo.phraseExtraInfoByLevel[i].redundant = i16_le(&plainText[j]);
      j += 2;
      sngInfo.phraseExtraInfoByLevel[i].padding = plainText[j];
      j += 1;
    }
  }

  {
    const i32 nLinkedDifficultyCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.nLinkedDifficulty.resize(nLinkedDifficultyCount);
    for (i32 i = 0; i < nLinkedDifficultyCount; ++i)
    {
      sngInfo.nLinkedDifficulty[i].levelBreak = i32_le(&plainText[j]);
      j += 4;
      sngInfo.nLinkedDifficulty[i].phraseCount = i32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.nLinkedDifficulty[i].nLDPhrase.resize(sngInfo.nLinkedDifficulty[i].phraseCount);
        for (i32 ii = 0; ii < sngInfo.nLinkedDifficulty[i].phraseCount; ++ii)
        {
          sngInfo.nLinkedDifficulty[i].nLDPhrase[ii] = i32_le(&plainText[j]);
          j += 4;
        }
      }
    }
  }

  {
    const i32 actionCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.action.resize(actionCount);
    for (i32 i = 0; i < actionCount; ++i)
    {
      sngInfo.action[i].time = f32_le(&plainText[j]);
      j += 4;
      memcpy(&sngInfo.action[i].actionName, &plainText[j], 256);
      j += 256;
    }
  }

  {
    const i32 eventCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.event.resize(eventCount);
    for (i32 i = 0; i < eventCount; ++i)
    {
      sngInfo.event[i].time = f32_le(&plainText[j]);
      j += 4;
      memcpy(&sngInfo.event[i].eventName, &plainText[j], 256);
      j += 256;
    }
  }

  {
    const i32 toneCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.tone.resize(toneCount);
    for (i32 i = 0; i < toneCount; ++i)
    {
      sngInfo.tone[i].time = f32_le(&plainText[j]);
      j += 4;
      sngInfo.tone[i].toneId = i32_le(&plainText[j]);
      j += 4;
    }
  }

  {
    const i32 dnaCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.dna.resize(dnaCount);
    for (i32 i = 0; i < dnaCount; ++i)
    {
      sngInfo.dna[i].time = f32_le(&plainText[j]);
      j += 4;
      sngInfo.dna[i].dnaId = i32_le(&plainText[j]);
      j += 4;
    }
  }

  {
    const i32 sectionCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.section.resize(sectionCount);
    for (i32 i = 0; i < sectionCount; ++i)
    {
      memcpy(&sngInfo.section[i].name, &plainText[j], 32);
      j += 32;
      sngInfo.section[i].number = i32_le(&plainText[j]);
      j += 4;
      sngInfo.section[i].startTime = f32_le(&plainText[j]);
      j += 4;
      sngInfo.section[i].endTime = f32_le(&plainText[j]);
      j += 4;
      sngInfo.section[i].startPhraseIterationId = i32_le(&plainText[j]);
      j += 4;
      sngInfo.section[i].endPhraseIterationId = i32_le(&plainText[j]);
      j += 4;
      memcpy(&sngInfo.section[i].stringMask, &plainText[j], 36);
      j += 36;
    }
  }

  {
    const i32 arrangementCount = u32_le(&plainText[j]);
    j += 4;
    sngInfo.arrangements.resize(arrangementCount);
    for (i32 i = 0; i < arrangementCount; ++i)
    {
      sngInfo.arrangements[i].difficulty = i32_le(&plainText[j]);
      j += 4;
      const i32 anchorCount = u32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].anchors.resize(anchorCount);
        for (i32 ii = 0; ii < anchorCount; ++ii)
        {
          sngInfo.arrangements[i].anchors[ii].startBeatTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].anchors[ii].endBeatTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].anchors[ii].unk3_FirstNoteTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].anchors[ii].unk4_LastNoteTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].anchors[ii].fretId = plainText[j];
          j += 1;
          memcpy(&sngInfo.arrangements[i].anchors[ii].padding, &plainText[j], 3);
          j += 3;
          sngInfo.arrangements[i].anchors[ii].width = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].anchors[ii].phraseIterationId = i32_le(&plainText[j]);
          j += 4;
        }
      }
      const i32 anchorExtensionCount = u32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].anchorExtensions.resize(anchorExtensionCount);
        for (i32 ii = 0; ii < anchorExtensionCount; ++ii)
        {
          sngInfo.arrangements[i].anchorExtensions[ii].beatTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].anchorExtensions[ii].fretId = plainText[j];
          j += 1;
          sngInfo.arrangements[i].anchorExtensions[ii].unk2_0 = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].anchorExtensions[ii].unk3_0 = i16_le(&plainText[j]);
          j += 2;
          sngInfo.arrangements[i].anchorExtensions[ii].unk4_0 = plainText[j];
          j += 1;
        }
      }
      const i32 fingerprints1Count = u32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].fingerprints1.resize(fingerprints1Count);
        for (i32 ii = 0; ii < fingerprints1Count; ++ii)
        {
          sngInfo.arrangements[i].fingerprints1[ii].chordId = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints1[ii].startTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints1[ii].endTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints1[ii].unk3_FirstNoteTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints1[ii].unk4_LastNoteTime = f32_le(&plainText[j]);
          j += 4;
        }
      }
      const i32 fingerprints2Count = u32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].fingerprints2.resize(fingerprints2Count);
        for (i32 ii = 0; ii < fingerprints2Count; ++ii)
        {
          sngInfo.arrangements[i].fingerprints2[ii].chordId = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints2[ii].startTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints2[ii].endTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints2[ii].unk3_FirstNoteTime = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].fingerprints2[ii].unk4_LastNoteTime = f32_le(&plainText[j]);
          j += 4;
        }
      }
      const i32 noteCount = u32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].notes.resize(noteCount);
        for (i32 ii = 0; ii < noteCount; ++ii)
        {
          sngInfo.arrangements[i].notes[ii].noteMask = Sng::NoteMask(u32_le(&plainText[j]));
          j += 4;
          sngInfo.arrangements[i].notes[ii].noteFlags = Sng::NoteFlags(u32_le(&plainText[j]));
          j += 4;
          sngInfo.arrangements[i].notes[ii].hash = u32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].notes[ii].time = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].notes[ii].stringIndex = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].fretId = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].anchorFretId = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].anchorWidth = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].chordId = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].notes[ii].chordNotesId = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].notes[ii].phraseId = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].notes[ii].phraseIterationId = i32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].notes[ii].fingerPrintId[0] = i16_le(&plainText[j]);
          j += 2;
          sngInfo.arrangements[i].notes[ii].fingerPrintId[1] = i16_le(&plainText[j]);
          j += 2;
          sngInfo.arrangements[i].notes[ii].nextIterNote = i16_le(&plainText[j]);
          j += 2;
          sngInfo.arrangements[i].notes[ii].prevIterNote = i16_le(&plainText[j]);
          j += 2;
          sngInfo.arrangements[i].notes[ii].parentPrevNote = i16_le(&plainText[j]);
          j += 2;
          sngInfo.arrangements[i].notes[ii].slideTo = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].slideUnpitchTo = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].leftHand = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].tap = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].pickDirection = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].slap = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].pluck = plainText[j];
          j += 1;
          sngInfo.arrangements[i].notes[ii].vibrato = i16_le(&plainText[j]);
          j += 2;
          sngInfo.arrangements[i].notes[ii].sustain = f32_le(&plainText[j]);
          j += 4;
          sngInfo.arrangements[i].notes[ii].maxBend = f32_le(&plainText[j]);
          j += 4;
          const u32 bendDataCount = u32_le(&plainText[j]);
          j += 4;
          {
            sngInfo.arrangements[i].notes[ii].bendData.resize(bendDataCount);
            for (u32 iii = 0; iii < bendDataCount; ++iii)
            {
              sngInfo.arrangements[i].notes[ii].bendData[iii].time = f32_le(&plainText[j]);
              j += 4;
              sngInfo.arrangements[i].notes[ii].bendData[iii].step = f32_le(&plainText[j]);
              j += 4;
              sngInfo.arrangements[i].notes[ii].bendData[iii].unk3_0 = i16_le(&plainText[j]);
              j += 2;
              sngInfo.arrangements[i].notes[ii].bendData[iii].unk4_0 = plainText[j];
              j += 1;
              sngInfo.arrangements[i].notes[ii].bendData[iii].unk5 = plainText[j];
              j += 1;
            }
          }
        }
      }

      sngInfo.arrangements[i].phraseCount = i32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].averageNotesPerIteration.resize(sngInfo.arrangements[i].phraseCount);
        for (i32 ii = 0; ii < sngInfo.arrangements[i].phraseCount; ++ii)
        {
          sngInfo.arrangements[i].averageNotesPerIteration[ii] = f32_le(&plainText[j]);
          j += 4;
        }
      }
      sngInfo.arrangements[i].phraseIterationCount1 = i32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].notesInIteration1.resize(sngInfo.arrangements[i].phraseIterationCount1);
        for (i32 ii = 0; ii < sngInfo.arrangements[i].phraseIterationCount1; ++ii)
        {
          sngInfo.arrangements[i].notesInIteration1[ii] = i32_le(&plainText[j]);
          j += 4;
        }
      }
      sngInfo.arrangements[i].phraseIterationCount2 = i32_le(&plainText[j]);
      j += 4;
      {
        sngInfo.arrangements[i].notesInIteration2.resize(sngInfo.arrangements[i].phraseIterationCount2);
        for (i32 ii = 0; ii < sngInfo.arrangements[i].phraseIterationCount2; ++ii)
        {
          sngInfo.arrangements[i].notesInIteration2[ii] = i32_le(&plainText[j]);
          j += 4;
        }
      }
    }
  }

  {
    sngInfo.metadata.maxScore = f64_le(&plainText[j]);
    j += 8;
    sngInfo.metadata.maxNotesAndChords = f64_le(&plainText[j]);
    j += 8;
    sngInfo.metadata.maxNotesAndChordsReal = f64_le(&plainText[j]);
    j += 8;
    sngInfo.metadata.pointsPerNote = f64_le(&plainText[j]);
    j += 8;
    sngInfo.metadata.firstBeatLength = f32_le(&plainText[j]);
    j += 4;
    sngInfo.metadata.startTime = f32_le(&plainText[j]);
    j += 4;
    sngInfo.metadata.capoFretId = plainText[j];
    j += 1;
    memcpy(&sngInfo.metadata.lastConversionDateTime, &plainText[j], 32);
    j += 32;
    sngInfo.metadata.part = i16_le(&plainText[j]);
    j += 2;
    sngInfo.metadata.songLength = f32_le(&plainText[j]);
    j += 4;
    sngInfo.metadata.stringCount = i32_le(&plainText[j]);
    j += 4;
    {
      sngInfo.metadata.tuning.resize(sngInfo.metadata.stringCount);
      for (i32 ii = 0; ii < sngInfo.metadata.stringCount; ++ii)
      {
        sngInfo.metadata.tuning[ii] = i16_le(&plainText[j]);
        j += 2;
      }
    }
    sngInfo.metadata.unk11FirstNoteTime = f32_le(&plainText[j]);
    j += 4;
    sngInfo.metadata.unk12FirstNoteTime = f32_le(&plainText[j]);
    j += 4;
    sngInfo.metadata.maxDifficulty = i32_le(&plainText[j]);
    j += 4;
  }

  return sngInfo;
}

#endif // SHR3D_PSARC
