private void setDeathDrain(QbItemArray ar, NotesMarker[] markers) { if (ar.Items.Count != 0 && ar.Items[0].QbItemType == QbItemType.ArrayStruct) { foreach (QbItemBase qib in ar.Items[0].Items) { QbItemStruct hdr = qib as QbItemStruct; if (qib != null && hdr.ItemCount == 2 && hdr.Items[0].QbItemType == QbItemType.StructItemInteger && hdr.Items[1].QbItemType == QbItemType.StructItemQbKey) { if (((QbItemQbKey)hdr.Items[1]).Values[0] == QbKey.Create("boss_battle_begin_deathlick")) ((QbItemInteger)hdr.Items[0]).Values[0] = Math.Max((uint)this.Length - 11000, (uint)0); //10000 can be read from DeathLick in guitar_battle.qb } } } }
private void setMarkers(int[] frets, QbItemArray arr, QbFile text, NotesMarker[] markers) { int minNote = this.Notes.MinNoteOffsetSynced + _startPaddingMs + _fretPadding; QbItemStruct s; QbItemInteger i; QbItemQbKey q; QbKey qbKey; QbItemString txt; for (int c = text.Items.Count - 1; c > 0; c--) text.RemoveItem(text.Items[c]); if (arr.Items[0] is QbItemFloats) { QbItemStructArray newArr = new QbItemStructArray(arr.Root); newArr.Create(QbItemType.ArrayStruct); arr.AddItem(newArr); arr.RemoveItem(arr.Items[0]); } QbItemStructArray sa = (QbItemStructArray)arr.Items[0]; sa.Items.Clear(); NotesMarker marker; List<NotesMarker> mrk = new List<NotesMarker>(markers); if (mrk.Count > 0 && mrk[0].Offset > minNote) //some charts don't have sections at the start so you can't practice the start notes :-( { if (mrk[0].Offset > minNote + 5000) // if > 5secs then add new mrk.Insert(0, new NotesMarker("Start", minNote)); else //else move first marker back to start mrk[0].Offset = minNote; } for (int c = 0; c < mrk.Count; c++) { marker = mrk[c]; if (c < mrk.Count - 1 && mrk[c + 1].Offset < minNote) continue; //don't add sections at the start that would have no notes (crashes song??) qbKey = QbKey.Create(string.Format("{0}_markers_text_{1}", this.SongQb.Id, QbKey.Create(marker.Title).Crc.ToString("x").ToLower())); txt = new QbItemString(text); txt.Create(QbItemType.SectionString); txt.ItemQbKey = qbKey; txt.Strings = new string[] { marker.Title }; text.AddItem(txt); s = new QbItemStruct(arr.Root); s.Create(QbItemType.StructHeader); sa.AddItem(s); i = new QbItemInteger(arr.Root); i.Create(QbItemType.StructItemInteger); i.ItemQbKey = QbKey.Create("time"); i.Values = new uint[] { findNearestFret((uint)marker.Offset, frets) }; s.AddItem(i); q = new QbItemQbKey(arr.Root); q.Create(QbItemType.StructItemQbKeyString); q.ItemQbKey = QbKey.Create("marker"); q.Values = new QbKey[] { qbKey }; s.AddItem(q); } text.AlignPointers(); arr.Root.AlignPointers(); }
private int replaceNotesItems() { int notesLength = 0; //position of final notes item (last fret) QbKey song = this.SongQb.Id; string songName = song.Text; GameFile gf = _project.FileManager.File(_project.GameInfo.GetNotesFilename(song)); if (gf == null) return notesLength; //return 0, this prevents exceptions in the ISO tool which doesn't replace notes string notesPak = gf.LocalName; PakFormat pf = new PakFormat(notesPak, "", "", _project.GameInfo.PakFormatType); PakEditor pak = new PakEditor(pf); QbFile qb = pak.ReadQbFile(_project.GameInfo.GetNotesQbFilename(song)); //modify the mid file //clearAllQbItems(qb, this.Notes.GhItems, // QbKey.Create(string.Format("{0}_TimeSig", songName)), // QbKey.Create(string.Format("{0}_FretBars", songName)), // QbKey.Create(string.Format("{0}_Markers", songName))); QbFile qbText; if (!_project.GameInfo.MidTextInQbPak) qbText = pak.ReadQbFile(_project.GameInfo.GetNotesTextQbFilename(song)); else { qbText = _project.FileManager.QbPakEditor.ReadQbFile(_project.GameInfo.GetNotesTextQbFilename(song)); } QbItemArray ar; int[] ints; int offset; //calculate ms to add to wav to ensure notes don't start before the given time (in seconds) if (this.Notes.MinNoteOffsetSynced < this.MinMsBeforeNotesStart) _startPaddingMs = this.MinMsBeforeNotesStart - this.Notes.MinNoteOffsetSynced; this.Length = this.Audio.AudioLength + _startPaddingMs; offset = this.Notes.BaseFile.NonNoteSyncOffset + _startPaddingMs; int[] frets = (int[])this.Notes.BaseFile.Frets.Clone(); //clone in case class is holding reference for (int i = 0; i < frets.Length; i++) frets[i] += offset; //this function sets _fretPadding, another padding value. It adds padding to allow a properly spaced fret to be at position 0 frets = adjustFrets(frets); notesLength = frets[frets.Length - 1]; //set the track length to the notes length if it's longer if (this.Length < notesLength) this.Length = notesLength; else this.Length += _fretPadding; offset += _fretPadding; int[] timeSig = (int[])this.Notes.BaseFile.Parser.GetTimeSig().Clone(); //clone in case class is holding reference if (timeSig.Length == 0) timeSig = new int[] { 0, 4, 4 }; for (int i = 0; i < timeSig.Length; i += 3) timeSig[i] = (int)findNearestFret((uint)(timeSig[i] + offset), frets); timeSig[0] = 0; //if the first item is not 0 the song won't load??. NotesMarker[] markers = (NotesMarker[])this.Notes.BaseFile.Parser.GetNotesMarkers().Clone(); //clone in case class is holding reference for (int i = 0; i < markers.Length; i += 3) markers[i] = new NotesMarker(markers[i].Title, markers[i].Offset + offset); //perform deep clone int oldSustainTrigger; int sustainTrigger; int nextNote; int[] faceOffP1Ints = new int[0]; int[] faceOffP2Ints = new int[0]; GhNotesItem faceOffItem = null; //we have a face off item for each mapping, GH3 only has one per song, find the one with the hardest difficulty (They're always the same anyway) foreach (GhNotesItem ghi in this.Notes.GhItems) { if (ghi.IsMapped && ghi.MappedFileItem.FaceOffP1Count != 0 && ghi.MappedFileItem.FaceOffP2Count != 0) { //non generated overrides generated if (faceOffItem == null || faceOffItem.MappedFileItem.HasGeneratedFaceOff || (ghi.MappedFileItem.HasGeneratedFaceOff == faceOffItem.MappedFileItem.HasGeneratedFaceOff)) { if (faceOffItem == null || ((int)faceOffItem.Difficulty < (int)ghi.Difficulty && ghi.Type == NotesType.Guitar)) //guitar is more reliable faceOffItem = ghi; } } } if (faceOffItem != null) { offset = faceOffItem.MappedFileItem.SyncOffset + _startPaddingMs + _fretPadding; faceOffP1Ints = (int[])faceOffItem.MappedFileItem.FaceOffP1.Clone(); for (int i = 0; i < faceOffP1Ints.Length; i += 2) faceOffP1Ints[i] += offset; faceOffP2Ints = (int[])faceOffItem.MappedFileItem.FaceOffP2.Clone(); for (int i = 0; i < faceOffP2Ints.Length; i += 2) faceOffP2Ints[i] += offset; } foreach (GhNotesItem ghi in this.Notes.GhItems) { if (ghi.IsMapped) { oldSustainTrigger = ghi.MappedFileItem.SustainTrigger; sustainTrigger = (int)((float)(frets[1] - frets[0]) / 2F); ghi.MappedFileItem.SustainTrigger = sustainTrigger; //Offset notes offset = ghi.MappedFileItem.SyncOffset + _startPaddingMs + _fretPadding; ints = (int[])ghi.MappedFileItem.Notes.Clone(); //don't modify original notes int startFret = 0; for (int i = 0; i < ints.Length; i += 3) { ints[i] += offset; if (i + 3 < ints.Length) nextNote = ints[i + 3] + offset; else nextNote = 0; //loop to find fret length note is in to calculate if note is sustained int fpos; int fretLen = 0; bool isSustained = false; for (int c = startFret; c < frets.Length; c++) { if ((fpos = frets[c]) > ints[i] && c > 0) //careful fpos is assigned here { startFret = c; //next time start from here fretLen = fpos - frets[c - 1]; isSustained = ints[i + 1] > ((fretLen / 192.0) * (double)(192 >> 2)); //(fretLen / 192.0 == bpmUnit break; } } //clip sustained notes to GH3 mode if (isSustained) ints[i + 1] = setSustain(ints[i], ints[i + 1], nextNote, oldSustainTrigger / 2, sustainTrigger / 2, this.Notes.Gh3SustainClipping); } int[] notes = ints; //if this is a boss battle then remove notes that are not within the face off sections if (this.IsBoss && faceOffItem != null) ints = convertBossNotesToFaceOff(ints, ghi.Type == NotesType.Guitar ? faceOffP1Ints : faceOffP2Ints); ints = adjustNotes(ints); //merge any notes that are really close together //replace track notes ar = (QbItemArray)qb.FindItem(ghi.SongNotesQbKey, false); replaceQbItems(ar, ints, false); setLength(ar, this.Length, 3, frets, false); //Offset star power ints = (int[])ghi.MappedFileItem.StarPower.Clone(); //don't modify original starpower for (int i = 0; i < ints.Length; i += 3) ints[i] += offset; //if (faceOffItem != null) // alignBattleStarPowerToFaceOff(ints, ghi.Type == NotesType.Guitar ? faceOffP1Ints : faceOffP2Ints); ints = adjustBattleStarPower(ints, notes); //replace star power ar = (QbItemArray)qb.FindItem(ghi.SongStarPowerQbKey, false); replaceQbItems(ar, this.Notes.ForceNoStarPower ? new int[0] : ints, true); //optionally set to no star power setLength(ar, this.Length, 3, frets, false); int[] sp = ints; //Offset star battle mode ints = (int[])ghi.MappedFileItem.BattlePower.Clone(); for (int i = 0; i < ints.Length; i += 3) ints[i] += offset; //if (faceOffItem != null) // alignBattleStarPowerToFaceOff(ints, ghi.Type == NotesType.Guitar ? faceOffP1Ints : faceOffP2Ints); ints = adjustBattleStarPower(ints, notes); if (ints.Length == 0) //if no ints then use star power ints = (int[])sp.Clone(); //just to ensure there's no issue with 2 references pointing at the same array //replace star battle notes ar = (QbItemArray)qb.FindItem(ghi.SongStarPowerBattleQbKey, false); replaceQbItems(ar, ints, true); setLength(ar, this.Length, 3, frets, false); } else { ar = (QbItemArray)qb.FindItem(ghi.SongNotesQbKey, false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(ghi.SongStarPowerQbKey, false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(ghi.SongStarPowerBattleQbKey, false); clearQbItems(ar); } } ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_GuitarCoop_{1}", songName, NotesDifficulty.Easy.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_GuitarCoop_{1}", songName, NotesDifficulty.Medium.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_GuitarCoop_{1}", songName, NotesDifficulty.Hard.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_GuitarCoop_{1}", songName, NotesDifficulty.Expert.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_RhythmCoop_{1}", songName, NotesDifficulty.Easy.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_RhythmCoop_{1}", songName, NotesDifficulty.Medium.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_RhythmCoop_{1}", songName, NotesDifficulty.Hard.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Song_RhythmCoop_{1}", songName, NotesDifficulty.Expert.ToString())), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_BossBattleP1", songName)), false); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_BossBattleP2", songName)), false); clearQbItems(ar); //start with expert guitar and work down the difficulties to find a GHItem with Face off, copy in to BOss Battle also if (faceOffItem != null) { QbItemArray fo1 = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_FaceOffP1", songName)), false); QbItemArray fo2 = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_FaceOffP2", songName)), false); if (faceOffItem.IsMapped && faceOffItem.MappedFileItem != null && faceOffItem.MappedFileItem.FaceOffP1Count + faceOffItem.MappedFileItem.FaceOffP2Count != 0) { faceOffP1Ints = adjustFaceOff(faceOffP1Ints); replaceQbItems(fo1, faceOffP1Ints, true, 2); //ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_BossBattleP1", songName)), false); //replaceQbItems(ar, faceOffP1Ints, true, 2); faceOffP2Ints = adjustFaceOff(faceOffP2Ints); replaceQbItems(fo2, faceOffP2Ints, true, 2); //ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_BossBattleP2", songName)), false); //replaceQbItems(ar, faceOffP2Ints, true, 2); } else { clearQbItems(fo1); clearQbItems(fo2); } } ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_TimeSig", songName)), false); replaceQbItems(ar, timeSig, true); setLength(ar, this.Length, 3, frets, false); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_FretBars", songName)), false); replaceQbItems(ar, frets, false); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Markers", songName)), false); if (markers.Length != 0) setMarkers(frets, ar, qbText, markers); else calculateMarkers(frets, ar, qbText); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Scripts_Notes", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Anim_Notes", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Triggers_Notes", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Cameras_Notes", songName)), false); setLength(ar, this.Length, 3, frets, true); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Lightshow_Notes", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Crowd_Notes", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Drums_Notes", songName)), false); ////setLength(ar, this.Length, 3, frets, true); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Performance_Notes", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Scripts", songName)), false); if (this.SongQb.IsBoss) //set the death drain marker to the correct location setDeathDrain(ar, markers); else setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Anim", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Triggers", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Cameras", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Lightshow", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Crowd", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Drums", songName)), false); ////setLength(ar, this.Length, 3, frets, true); clearQbItems(ar); ar = (QbItemArray)qb.FindItem(QbKey.Create(string.Format("{0}_Performance", songName)), false); setLength(ar, this.Length, 3, frets, true); //clearQbItems(ar); qb.AlignPointers(); qb.IsValid(); qbText.AlignPointers(); qbText.IsValid(); long origPakLen = pak.FileLength; string tmpPak = string.Format("{0}_{1}", notesPak, Guid.NewGuid().ToString("N")); File.Copy(notesPak, tmpPak); if (_project.GameInfo.MidTextInQbPak) { //save markers to qbPak _project.FileManager.QbPakEditor.ReplaceFile(_project.GameInfo.GetNotesTextQbFilename(song), qbText); } else //save markers to mid pak { pak.ReplaceFile(_project.GameInfo.GetNotesTextQbFilename(song), qbText); } //replace notes to mid pak pak.ReplaceFile(_project.GameInfo.GetNotesQbFilename(song), qb); //pad notes pak with original file //add padding to ensure song works - This took over a month of testing to find!! //////////using (FileStream fs = new FileStream(notesPak, FileMode.Open, FileAccess.ReadWrite)) //////////{ ////////// using (FileStream fsI = new FileStream(tmpPak, FileMode.Open, FileAccess.Read)) ////////// { ////////// fs.Seek(0, SeekOrigin.End); ////////// string tag = " -=> NANOOK <=- PADDING BELOW "; ////////// if (fs.Position + tag.Length < fsI.Length) ////////// fs.Write(Encoding.Default.GetBytes(tag), 0, tag.Length); ////////// if (fs.Position < fsI.Length) ////////// { ////////// fsI.Seek(fs.Position, SeekOrigin.Begin); ////////// copy(fsI, fs, fsI.Length - fsI.Position); ////////// } ////////// } //////////} FileHelper.Delete(tmpPak); return notesLength; }