/*
* The MIT License
*
* Copyright 2015 Neil McAlister.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package dotaSoundEditor.Controls;
import dotaSoundEditor.Helpers.*;
import dotaSoundEditor.NamedMusic;
import info.ata4.vpk.VPKArchive;
import info.ata4.vpk.VPKEntry;
import java.awt.event.ItemEvent;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.tree.TreeModel;
public final class MusicPanel extends EditorPanel
{
Executor e = Executors.newSingleThreadExecutor();
public MusicPanel()
{
initComponents();
}
public MusicPanel(String _vpkPath, String _installDir, CacheManager _cm, SoundPlayer _sp)
{
vpkPath = _vpkPath;
installDir = _installDir;
this.setName("Music");
initComponents();
soundPlayer = _sp;
cacheManager = _cm;
currentDropdown = musicDropdown;
currentTree = musicTree;
this.populateDropdownBox();
this.populateSoundList();
attachDoubleClickListenerToTree();
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents()
{
jMenuBar1 = new javax.swing.JMenuBar();
jMenu1 = new javax.swing.JMenu();
jMenu2 = new javax.swing.JMenu();
musicLabel = new javax.swing.JLabel();
jScrollPane2 = new javax.swing.JScrollPane();
musicTree = new javax.swing.JTree();
musicDropdown = new javax.swing.JComboBox();
jMenu1.setText("File");
jMenuBar1.add(jMenu1);
jMenu2.setText("Edit");
jMenuBar1.add(jMenu2);
musicLabel.setText("Music Type:");
jScrollPane2.setViewportView(musicTree);
musicDropdown.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
musicDropdown.addItemListener(new java.awt.event.ItemListener()
{
public void itemStateChanged(java.awt.event.ItemEvent evt)
{
musicDropdownItemStateChanged(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(musicLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(musicDropdown, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 465, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(musicLabel)
.addComponent(musicDropdown, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(70, 70, 70)
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 383, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void musicDropdownItemStateChanged(java.awt.event.ItemEvent evt)//GEN-FIRST:event_musicDropdownItemStateChanged
{//GEN-HEADEREND:event_musicDropdownItemStateChanged
if (evt.getStateChange() == ItemEvent.SELECTED)
{
if (!getAdvancedMode())
{
//In a background thread so the app doesn't choke on fast scroling
e.execute(new Runnable()
{
@Override
public void run()
{
populateSoundList();
}
});
}
else
{
String scriptPath = getCurrentScriptString();
ScriptParser parser = new ScriptParser(new File(Paths.get(scriptPath).toString()));
TreeModel model = parser.getTreeModel();
currentTree.setModel(model);
currentTree.setEditable(true);
for (int i = 0; i < currentTree.getRowCount(); i++)
{
currentTree.expandRow(i);
}
}
currentTree.setRootVisible(false);
currentTree.setShowsRootHandles(true);
}
}//GEN-LAST:event_musicDropdownItemStateChanged
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JMenu jMenu1;
private javax.swing.JMenu jMenu2;
private javax.swing.JMenuBar jMenuBar1;
private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JComboBox musicDropdown;
private javax.swing.JLabel musicLabel;
private javax.swing.JTree musicTree;
// End of variables declaration//GEN-END:variables
@Override
void fillImageFrame(Object selectedItem) throws IOException
{
} //This panel has no image Frame, implementation is unecessary
@Override
void populateDropdownBox()
{
currentDropdown.removeAllItems();
ArrayList<String> vpkSearchPaths = new ArrayList<>();
ArrayList<NamedMusic> namedMusicList = new ArrayList<>();
vpkSearchPaths.add("scripts/music/");
vpkSearchPaths.add("scripts/music/terrorblade_arcana/");
vpkSearchPaths.add("scripts/music/valve_dota_001/");
vpkSearchPaths.add("scripts/music/valve_ti4/");
File file = new File(vpkPath);
VPKArchive vpk = new VPKArchive();
try
{
vpk.load(file);
}
catch (Exception ex)
{
System.err.println("Can't open archive: " + ex.getMessage());
return;
}
for (String dir : vpkSearchPaths)
{
for (VPKEntry entry : vpk.getEntriesForDir(dir))
{
//TODO: Replace this with a (user-editable?) list of paths to search read in from a file(?)
//Music is scattered in a bunch of different directories, so look in all of them
//Pretty brittle right now, thus the TODO up there
if (entry.getPath().contains("game_sounds_music.txt")
|| entry.getPath().contains("game_sounds_music_int.txt")
|| entry.getPath().contains("game_sounds_music_spectator.txt")
|| entry.getPath().contains("game_sounds_music_tutorial.txt")
|| entry.getPath().contains("game_sounds_stingers.txt")
|| entry.getPath().contains("game_sounds_music_util.txt")
|| entry.getPath().contains("game_sounds_stingers_diretide.txt")
|| entry.getPath().contains("game_sounds_stingers_greevil.txt")
|| entry.getPath().contains("game_sounds_stingers_main.txt"))
{
//format internal name a little bit, remove prefixes
int lastSlashIndex = entry.getDir().lastIndexOf("/");
int firstSlashIndex = entry.getDir().substring(0, lastSlashIndex - 1).lastIndexOf("/");
String parentDir = entry.getDir().substring(firstSlashIndex + 1, lastSlashIndex);
String internalName = parentDir + "/" + entry.getName();
NamedMusic nm = new NamedMusic(internalName, entry.getPath());
namedMusicList.add(nm);
}
}
}
Collections.sort(namedMusicList);
for (NamedMusic nm : namedMusicList)
{
currentDropdown.addItem(nm);
}
}
@Override
void populateSoundList()
{
currentTree.setEditable(false);
inAdvancedMode = false;
File scriptFile = new File(getCurrentScriptString());
String scriptKey = ((NamedMusic) currentDropdown.getSelectedItem()).getInternalName().toLowerCase() + ".txt";
VPKEntry entry;
boolean needsValidation = false;
if (!scriptFile.isFile())
{
String currentMusicPath = ((NamedMusic) currentDropdown.getSelectedItem()).getFilePath().toString();
entry = getMusicScriptFile(currentMusicPath);
writeScriptFileToDisk(entry, false);
scriptFile = new File(getCurrentScriptString());
this.updateCache(scriptKey, entry.getCRC32());
}
else
{
needsValidation = true;
}
ScriptParser parser = new ScriptParser(scriptFile);
TreeModel scriptTree = parser.getTreeModel();
if (needsValidation)
{
String internalScriptPath = ((NamedMusic) currentDropdown.getSelectedItem()).getFilePath().toString().toLowerCase();
internalScriptPath = internalScriptPath.replace("\\", "/");
boolean isUpToDate = this.validateScriptFile(scriptKey, internalScriptPath);
if (!isUpToDate)
{
this.writeScriptFileToDisk(cacheManager.getCachedVpkEntry(), true);
mergeNewChanges(scriptTree, scriptFile);
this.updateCache(cacheManager.getCachedVpkEntry().getName() + ".txt", cacheManager.getCachedVpkEntry().getCRC32());
}
}
this.currentTreeModel = scriptTree;
currentTree.setModel(buildSoundListTree(scriptTree));
currentTree.setRootVisible(false);
currentTree.setShowsRootHandles(true);
}
@Override
String getCurrentScriptString()
{
if (currentDropdown.getSelectedItem() != null)
{
String internalPath = ((NamedMusic) currentDropdown.getSelectedItem()).getFilePath().toString();
String scriptPathString = Paths.get(installDir, "dota", internalPath).toString();
if (new File(scriptPathString).isFile())
{
return new File(scriptPathString).getAbsolutePath();
}
}
else
{
return "";
}
return "";
}
@Override
String getCustomSoundPathString()
{
return "custom/music/";
}
private VPKEntry getMusicScriptFile(String fileName)
{
File vpkFile = new File(vpkPath);
VPKArchive vpk = new VPKArchive();
VPKEntry entryToReturn = null;
try
{
vpk.load(vpkFile);
}
catch (Exception ex)
{
System.err.println("Can't open VPK Archive. Details: " + ex.getMessage());
return entryToReturn;
}
String fn = fileName.replace("\\", "/"); //jVPKLib only uses forward slashes in paths.
return vpk.getEntry(fn);
}
@Override
void updateCache(String scriptKey, long internalCrc)
{
String internalPath = ((NamedMusic) currentDropdown.getSelectedItem()).getFilePath().toString();
internalPath = internalPath.replace("\\", "/");
cacheManager.putScript(scriptKey, internalPath, internalCrc);
}
}