How to read data from Unity3D's prefab file? - c#

So, I am trying to create an test application for online Unity game but to do what I need it to do, I must read datas from a hex offset located in a .prefab file.
And then, i will load prefabs on server-side from data. For an anti-collider hack from client-side.
I must read datas like unityVer, localScale, localPosition, localSize, collider size... etc.
Edit: I must make it on a simple console application, not in Unity.
My not working code :
try {
string[] prefabFiles = Directory.GetFiles(Path.Combine(Environment.CurrentDirectory, "data\\prefabs\\"));
foreach (var prefab in prefabFiles) {
if (prefab != null && prefab.Split('.')[1] == "prefab") {
try {
using (var reader = new BinaryReader(new FileStream(prefab, FileMode.Open, FileAccess.Read))) {
FileInfo info = new FileInfo(prefab);
Console.WriteLine("Analyzing File : " + info.Name);
reader.BaseStream.Position = 0xA;
//string unityVer = reader...
//float localPositionX = reader...
//float localPositionY = reader...
//float localPositionZ = reader...
//float localScaleX = reader...
//float localScaleY = reader...
//float localScaleZ = reader...
//float colliderRadius = reader...
}
Console.WriteLine("File parse success.");
} catch { Console.WriteLine("Parse error in the data file."); }
}
}
} catch (Exception e_Ex) {
Console.WriteLine(e_Ex.ToString());
}
My Prefab's Hex Tree:
HEX TREE IMAGE
I am new to reading and writing data located in a hex data location so any help would be awesome!

A partial answer would be to save assets as YAML text files instead of binary. That format is human readable, which makes it much easier to decode. Under Project Settings -> Editor, there's Asset Serialization, which should be set to Force Text. Of course all existing assets are going to be re-imported and existing builds will have to be re-deployed.

I think you are trying too hard or I misunderstood your point.
When you have a prefab that is referenced in your project, Unity will create an instance of it, it just won't show in the scene and is kept internally. Then when you use Instantiate with it, it will duplicate all info and data.
That means you can access info on prefab the same way you would access them on any game object:
public GameObject myPrefab;
void Start(){
Debug.Log(myPrefab.transform.position);
Collider col = myPrefab.GetComponent<Collider>();
// Use Collider
}

Related

Empty array if strings in c# for unity game

I am trying to empty and array of strings every-time a user clicks on load profiles so that the array doesn't get over populated with the same profiles over and over again every time they click load.
This is my array:
string[] files;
This is how i get the profiles but i need to empty it whenever getProfiles is called so it doesn't over populate with duplicate profiles
private void GetProfiles()
{
//Check if directory with saved profiles exists
if (Directory.Exists(filePath))
{
//Clear files array
//Get all of the file paths of each file in this directory
files = Directory.GetFiles(filePath);
//profileTileTemplate.SetActive(true);
//Go through each file and read the data and create a profile for each json file
for(int i = 1; i <= files.Length -1; i++)
{
string json = File.ReadAllText(files[i]);
Profile savedProfile = JsonUtility.FromJson<Profile>(json);
Instantiate(profileTileTemplate,LoadProfileTransform);
profileTileTemplate.SetActive(true);
profileTileTemplate.GetComponentInChildren<Text>().text = savedProfile.playerName;
Debug.Log(savedProfile.playerName);
}
}
}
how do i clear this array?
I have tried setting it to null but it doesn't work.
Question is not clear, but if you want to empty the array and stay with the same length then you can write this wherever you want to.
files = new string[files.Length];
As said I don't think this is related to the array at all .. GetFiles everytime returns a new array there are no duplicates.
However, you Instantiate a bunch of objects into the scene and never destroy them!
You could do this first
foreach(Transform child in LoadProfileTransform)
{
Destroy (child.gameObject);
}
and then you could simply do
foreach(var path in files)
{
var json = File.ReadAllText(path);
var savedProfile = JsonUtility.FromJson<Profile>(json);
var obj = Instantiate(profileTileTemplate,LoadProfileTransform);
obj.SetActive(true);
obj.GetComponentInChildren<Text>().text = savedProfile.playerName;
Debug.Log(savedProfile.playerName);
}
since you want to apply the text etc to the newly instantiated objects, not the original prefab.

Select Random line from Text File and display on unity

I was creating a form which contains contact details on unity for a game. I managed to connect text file with unity and write lines to it but I am not sure how to get a random person from text file and display it on unity. If someone has any idea about this please help me out..
If you link your file via e.g
public TextAsset file;
then you could e.g. do
private string GetRandomLine()
{
var lines = file.text.Split('/n');
var randomIndex = Random.Range(0, lines.Length);
return lines[randomIndex];
}
Or if you use system FileIo you can do
var lines = File.ReadAllLines(path);
var randomIndex = Random.Range(0, lines.Length);
return lines[randomIndex];
Of course you might want to cache the file content and lines somewhere etc but I hope the idea gets clear.

How can I do reading and writing TextAssets at runtime with Unity 3D?

I have my desired txt files which I want to use as TextAssets. I need these files to be usable at runtime by my other scripts. Now the issue is that I can not figure out a way to make these things work.
I know that I should be using the Assets/Resources or the Streaming Assets folder but for some reason things are not working properly. Is there a way to incorporate it all with StreamWriters and Filestreams? What about TextAssets assigned in Unity Editor, can those also be setup as Streaming?
Some examples of code that uses my assets:
public void TaskOnClick() //getting multi-values
{
string filename = "Assets/Resources/TempoText/multi-export.txt";
using (StreamWriter writeFile = new StreamWriter(filename, false))
{
foreach (string inputJson in File.ReadLines("Assets/Resources/TempoText/multi-import.txt"))
{
string temperature = GetTemperatureByRegex(inputJson);
Debug.Log(temperature);
writeFile.AutoFlush = true;
Console.SetOut(writeFile);
writeFile.WriteLine(temperature.ToString());
}
}
File.Copy("Assets/Resources/TempoText/multi-export.txt", "Assets/Resources/multi-export.txt", true);
}
//or
FileStream filestream = new FileStream("Assets/Resources/TempoText/multi-import.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
var writeFile = new StreamWriter(filestream);
{
var document = collection.Find(new BsonDocument()).Sort(sort).Limit(limit: limit).ForEachAsync(d => Console.WriteLine(d)); //displays last 10 entries
Debug.Log(document.ToString());
writeFile.AutoFlush = true;
Console.SetOut(writeFile);
writeFile.Write(document.ToString());
}
All help greatly appreciated, I've basically messed up big time since I only found out about this now when I built everything as is...
Edit: got the streamwriters to do everything nicely with Application.persistentDataPath! Now stuck with a problem that I already struggled with - how to assign a TextAsset to get the file from a fixed path...
public TextAsset textFile;
Wondering how to set this to get it's .txt from Application.persistentDataPath
Application.persistentDataPath is what was needed all along.
Something nobody ever mentioned wherever I looked around. Hope somebody will be able to find the correct way using this mess of a question and lackluster answer.

Play .wma from memory stream using NAudio

I'm working on a C# app in Visual Studio 2013 that needs to play audio files in .wav, .mp3 and .wma formats. the .wav and mp3 files play with no problem. .wma files, however, seem to require extra handling and I'm at a loss to find a solution.
Here are the using statements at the top of the project file:
using NAudio;
using NAudio.Wave;
using NAudio.FileFormats.Wav;
using NAudio.FileFormats.Mp3;
using NAudio.WindowsMediaFormat;
using NAudio.MediaFoundation;
And here's the code for playback:
private void PlayIntroScreenAudio()
{
Player.Stop();
byte[] IntroAudioInBytes = Convert.FromBase64String(GameInfo.IntroScreenAudio);
MemoryStream msIntroAudioStream = new MemoryStream(IntroAudioInBytes, 0, IntroAudioInBytes.Length);
msIntroAudioStream.Write(IntroAudioInBytes, 0, IntroAudioInBytes.Length);
msIntroAudioStream.Seek(0, SeekOrigin.Begin);
msIntroAudioStream.Position = 0;
if (GameInfo.IntroScreenAudioFileExt == ".wav")
{
WaveFileReader wfr = new WaveFileReader(msIntroAudioStream);
Player.Init(wfr);
}
else if (GameInfo.IntroScreenAudioFileExt == ".mp3")
{
Mp3FileReader mp3rdr = new Mp3FileReader(msIntroAudioStream);
Player.Init(mp3rdr);
}
else if (GameInfo.IntroScreenAudioFileExt == ".wma")
{
WMAFileReader wmafr = new WMAFileReader(msIntroAudioStream);
Player.Init(wmafr);
}
Player.Play();
IntroAudioIsPlaying = true;
FinalScoreAudioIsPlaying = QuestionAudioIsPlaying = CARAudioIsPlaying = IARAudioIsPlaying = false;
btnPlayIntroScreenAudio.Image = Properties.Resources.btnStopIcon;
btnPlayFinalScoreAudio.Image = btnPlayQuestionAudio.Image = btnPlayCorrectResponseAudio.Image =
btnPlayIncorrectResponseAudio.Image = Properties.Resources.btnPlayIcon;
Player.PlaybackStopped += Player_PlaybackStopped;
}
As you'll probably guess, I get a wiggly line under "(msIntroAudioStream)". I tried adding ".ToString() inside the parentheses, but VS says it's wrong, since wmafr can't read from a string. What other code do I need to play a .wma file?
WMAFileReader only supports input from a file and it expects a string representing a path to file in its constructor's argument.
If you want to use WMAFileReader, you would have to write your MemoryStream to a file first, and then feed the path to WMAFileReader.
Curiously enough, WMAFileReader has no constructor taking a Stream as an argument, but Mp3FileReader and WaveFileReader both do.

Trouble playing mp3s after id3 image edit

Due to hardware restrictions, the software we produce tries to ensure that any audio file it imports into it's library (ready to be copied onto the hardware) is an acceptable bit rate.
Recently we've started using FFmpeg to convert a number of different audio types to mp3 to allow them to be imported and used on our hardware. Whilst the conversion works fine and the mp3 files work on our hardware afterwards, we're having issues specifically when adding an album art to the ID3 tags of the mp3. The track will not play audio in our software. It also seems that Windows cannot pick up the values of the ID3 tags in explorer, but Windows Media Player will still play the track.
This problem only seems to occur when changing a newly converted mp3s' ID3 tags after using FFmpeg. Changing tags on mp3s from other sources or those that have already got ID3 tag album art is fine.
The code for using FFmpeg from our software is as follows:
private const string SAMPLE_RATE = "44100";
...
//create temp file for output
outFile = Path.GetTempFileName();
outFile = Path.ChangeExtension(outFile, "mp3");
if (!File.Exists(inFile))
return false;
string metadata = (inFile.EndsWith("mp3")) ? " " : " -map_meta_data 0:0 ";
//build process
string workingDirectory = Environment.CurrentDirectory;
ProcessStartInfo FFmpegProcessInfo = new ProcessStartInfo();
FFmpegProcessInfo.WorkingDirectory = workingDirectory;
FFmpegProcessInfo.FileName = "ffmpeg.exe";
FFmpegProcessInfo.Arguments = "-i \"" + inFile + "\"" + " -ar "+SAMPLE_RATE + metadata + "\"" + outFile + "\""; //default conversion to SAMPLE_RATE
FFmpegProcessInfo.CreateNoWindow = true; //hide from user
//let us grab the output
FFmpegProcessInfo.RedirectStandardError = true;
FFmpegProcessInfo.RedirectStandardOutput = true;
FFmpegProcessInfo.UseShellExecute = false;
Process p = Process.Start(FFmpegProcessInfo);
To change the ID3 tags we have started using TagLib-Sharp and the code for changing the ID3 tags is:
public void SetId3Tags(string path, Bitmap image, IDictionary<string, string> values)
{
FileInfo fileInfo = new FileInfo(path);
fileInfo.Attributes = FileAttributes.Normal;
try
{
TagLib.File tagFile = TagLib.File.Create(path);
if (values.ContainsKey("Title"))
tagFile.Tag.Title = values["Title"];
if (values.ContainsKey("Artist"))
tagFile.Tag.Performers = new string[1] { values["Artist"] };
if (values.ContainsKey("Comments"))
tagFile.Tag.Comment = values["Comments"];
if (image != null) {
string tmpImg = Path.GetTempFileName();
image.Save(tmpImg);
IPicture newArt = new Picture(tmpImg);
tagFile.Tag.Pictures = new IPicture[1] {newArt};
}
tagFile.Save();
}
catch (Exception e)
{
_logger.Log(e);
}
}
And the code used to play the track in the software (FilgraphManager in QuartzTypeLib):
public void Play()
{
if (!_isPaused)
{
_graphManager = new FilgraphManager();
_mp3control = (IMediaControl)_graphManager;
_mp3position = (IMediaPosition)_graphManager;
_tempFile = Path.GetTempFileName();
File.Copy(_fullPath, _tempFile, true);
_mp3control.RenderFile(_tempFile);
}
else
{
_isPaused = false;
}
_mp3control.Run();
}
And the error when executing _mp3control.RenderFile(_tempFile):
{System.Runtime.InteropServices.ExternalException} = {"Exception from HRESULT: 0x80040266"}
at QuartzTypeLib.FilgraphManagerClass.RenderFile(String strFilename)
My largest problem here is that I don't know whether the fault lies with (our implementation of) FFmpeg (large library that's used fine in many other places), TagLib-Sharp or the audio playing.
Edit 1: Following J. Andrew Laughlin's advice I've been looking at the differences of the ID3 tags in the hex of each file. This is what I've found:
The initial input is ID3v2.3. After re-encoding with FFmpeg, the ID3 data is v2.4. This initial re-encoded file plays fine in media players and our software. Using TagLib# in our software to add album art keeps ID3v2.4 but the tags are only available using TagLib# to read them and it only plays in media players such as Windows Media Player. Using another tool to change the ID3 tags (in this case AudioShell Tag Editor) and add the same album art changed the ID3 version to 2.3 and meant that the mp3 played on our softwares audio player as well as other media players - However changing the tags afterwards produces an exception when saving the image.
One other thing I tried was to rip out the ID3v2.4 block completely after the re-encoding, this plays (as you'd expect) in all media players. When using the TagLib# on this untagged file, the tags were correctly applied (v2.3) and it continued to play properly in our software as well as others.
Unless anyone can suggest an elegant solution (either force TagLib# to write a new ID3v2.3 block or stop FFmpeg from writing one at all) I think I may just programmatically remove the ID3v2.4 block from the file after encoding and then write a new one.
TagLib# can be used to "downgrade" an ID3 tag from 2.4 to 2.3. I personally prefer to convert my ID3 tags to 2.3 since it is more consistently adopted across music players.
It's been a while, but I believe you can use the following in your above code:
TagLib.Id3v2.Tag id3v2tag = tagFile.GetTag(TagLib.TagTypes.Id3v2, false);
if(id3v2tag != null)
id3v2tag.Version = 3;
tagFile.Save();
Alternatively, you can force all tags to render in 2.3 by using the following code when your application initializes:
TagLib.Id3v2.Tag.DefaultVersion = 3;
TagLib.Id3v2.Tag.ForceDefaultVersion = true;
TagLib# can also remove tags completely and re-add them, but it shouldn't have to come to that.
Good luck!

Categories

Resources